diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/AutoTypeSearch/Throbber.gif b/AutoTypeSearch/Throbber.gif
new file mode 100755
index 0000000..494d426
--- /dev/null
+++ b/AutoTypeSearch/Throbber.gif
Binary files differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/AutoTypeSearch/Throbber.gif b/AutoTypeSearch/Throbber.gif
new file mode 100755
index 0000000..494d426
--- /dev/null
+++ b/AutoTypeSearch/Throbber.gif
Binary files differ
diff --git a/AutoTypeSearch/app.config b/AutoTypeSearch/app.config
new file mode 100755
index 0000000..1370758
--- /dev/null
+++ b/AutoTypeSearch/app.config
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/AutoTypeSearch/Throbber.gif b/AutoTypeSearch/Throbber.gif
new file mode 100755
index 0000000..494d426
--- /dev/null
+++ b/AutoTypeSearch/Throbber.gif
Binary files differ
diff --git a/AutoTypeSearch/app.config b/AutoTypeSearch/app.config
new file mode 100755
index 0000000..1370758
--- /dev/null
+++ b/AutoTypeSearch/app.config
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
+
\ No newline at end of file
diff --git a/CreatePlgX.bat b/CreatePlgX.bat
new file mode 100755
index 0000000..59b9aa0
--- /dev/null
+++ b/CreatePlgX.bat
@@ -0,0 +1,20 @@
+@echo off
+cd %~dp0
+
+echo Deleting existing PlgX folder
+rmdir /s /q PlgX
+
+echo Creating PlgX folder
+mkdir PlgX
+
+echo Copying files
+xcopy "AutoTypeSearch" PlgX /s /e /exclude:PlgXExclude.txt
+
+echo Compiling PlgX
+"../KeePass/KeePass.exe" /plgx-create "%~dp0PlgX" --plgx-prereq-kp:2.27
+
+echo Releasing PlgX
+move /y PlgX.plgx "Releases\Build Outputs\AutoTypeSearch.plgx"
+
+echo Cleaning up
+rmdir /s /q PlgX
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/AutoTypeSearch/Throbber.gif b/AutoTypeSearch/Throbber.gif
new file mode 100755
index 0000000..494d426
--- /dev/null
+++ b/AutoTypeSearch/Throbber.gif
Binary files differ
diff --git a/AutoTypeSearch/app.config b/AutoTypeSearch/app.config
new file mode 100755
index 0000000..1370758
--- /dev/null
+++ b/AutoTypeSearch/app.config
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
+
\ No newline at end of file
diff --git a/CreatePlgX.bat b/CreatePlgX.bat
new file mode 100755
index 0000000..59b9aa0
--- /dev/null
+++ b/CreatePlgX.bat
@@ -0,0 +1,20 @@
+@echo off
+cd %~dp0
+
+echo Deleting existing PlgX folder
+rmdir /s /q PlgX
+
+echo Creating PlgX folder
+mkdir PlgX
+
+echo Copying files
+xcopy "AutoTypeSearch" PlgX /s /e /exclude:PlgXExclude.txt
+
+echo Compiling PlgX
+"../KeePass/KeePass.exe" /plgx-create "%~dp0PlgX" --plgx-prereq-kp:2.27
+
+echo Releasing PlgX
+move /y PlgX.plgx "Releases\Build Outputs\AutoTypeSearch.plgx"
+
+echo Cleaning up
+rmdir /s /q PlgX
diff --git a/PlgXExclude.txt b/PlgXExclude.txt
new file mode 100755
index 0000000..f626d58
--- /dev/null
+++ b/PlgXExclude.txt
@@ -0,0 +1,7 @@
+\bin\
+\obj\
+.user
+.sln
+.suo
+.pdb
+.xml
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/AutoTypeSearch/Throbber.gif b/AutoTypeSearch/Throbber.gif
new file mode 100755
index 0000000..494d426
--- /dev/null
+++ b/AutoTypeSearch/Throbber.gif
Binary files differ
diff --git a/AutoTypeSearch/app.config b/AutoTypeSearch/app.config
new file mode 100755
index 0000000..1370758
--- /dev/null
+++ b/AutoTypeSearch/app.config
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
+
\ No newline at end of file
diff --git a/CreatePlgX.bat b/CreatePlgX.bat
new file mode 100755
index 0000000..59b9aa0
--- /dev/null
+++ b/CreatePlgX.bat
@@ -0,0 +1,20 @@
+@echo off
+cd %~dp0
+
+echo Deleting existing PlgX folder
+rmdir /s /q PlgX
+
+echo Creating PlgX folder
+mkdir PlgX
+
+echo Copying files
+xcopy "AutoTypeSearch" PlgX /s /e /exclude:PlgXExclude.txt
+
+echo Compiling PlgX
+"../KeePass/KeePass.exe" /plgx-create "%~dp0PlgX" --plgx-prereq-kp:2.27
+
+echo Releasing PlgX
+move /y PlgX.plgx "Releases\Build Outputs\AutoTypeSearch.plgx"
+
+echo Cleaning up
+rmdir /s /q PlgX
diff --git a/PlgXExclude.txt b/PlgXExclude.txt
new file mode 100755
index 0000000..f626d58
--- /dev/null
+++ b/PlgXExclude.txt
@@ -0,0 +1,7 @@
+\bin\
+\obj\
+.user
+.sln
+.suo
+.pdb
+.xml
\ No newline at end of file
diff --git a/Readme.txt b/Readme.txt
new file mode 100755
index 0000000..96d2fc0
--- /dev/null
+++ b/Readme.txt
@@ -0,0 +1,103 @@
+AutoTypeSearch
+==============
+http://sourceforge.net/projects/autotypesearch
+
+
+This is a plugin to KeePass to provide a quick searching capability as
+an enhancement to the global auto-type system. If a global auto-type is requested, but no matching
+entry for the active window is found, this plugin will show a quick as-you-type search window which
+lets you to easily pick the entry to auto-type.
+
+
+Installation
+------------
+Place AutoTypeSearch.plgx in your KeePass Plugins folder.
+
+
+Usage
+-----
+AutoTypeSearch is initially configured to automatically appear after an unsuccessful global
+auto-type. However, this can be changed in the KeePass Options window (an AutoTypeShow tab has
+been added). Here, a system-wide hot key can be configured to show the AutoTypeSearch window
+immediately. It is also possible to show the window by running KeePass.exe passing "/e1:AutoTypeSearch"
+as a command line parameter.
+
+Once the window is shown, usage is extremely simple. Just start typing, and AutoTypeSearch will
+search your database for matching entries. By default, the Title, Url, Notes, Tags, and Custom Fields
+will be searched, but this can be configured in the AutoTypeShow tab of the KeePass Options window.
+
+Protected fields (like Password) will not be searched.
+
+The arrow keys can be used to move the selection in the list of results, then press Enter to auto-
+type the selected entry. Alternatively, press Shift+Enter to open the entry instead of auto-typing it.
+(These actions can also be customised in the Options window.) Clicking and Shift-Clicking an entry will
+also perform those actions.
+
+
+Uninstallation
+--------------
+Delete AutoTypeSearch.plgx from your KeePass Plugins folder.
+
+
+Checking for updates
+--------------------
+If you want to use the KeePass Check for Updates function to check for updates to this plugin
+then it requires the SourceForgeUpdateChecker plugin to be installed too:
+http://sourceforge.net/projects/kpsfupdatechecker
+
+
+Bug Reporting, Questions, Comments, Feedback, Donations
+-------------------------------------------------------
+Please use the SourceForge project page:
+Bugs can be reported using the issue tracker, for anything else, a discussion forum is available.
+
+
+Changelog
+---------
+v0.1
+ Initial release
+
+v0.2
+ Added information banner when search is shown as a result of an unsuccessful global auto-type
+ Compatibility with Linux/Mono
+
+v0.3
+ Added search result prioritisation for entries where the match is found at the start of the field
+
+v0.4
+ Added support for multiple databases. All currently open, unlocked, databases will be searched
+
+v0.5
+ Added support for KeePass 2.29 high resolution custom icons
+
+v0.6
+ Where title does not uniquely identify the results shown, now also shows the group name as context
+
+v0.7
+ Added support for the "Open entry URL" action. Use the Options window to choose this, if required.
+
+v0.8
+ Added support for the "Copy password" action. Use the Options window to choose this, if required.
+
+v0.9
+ Added workaround for mono bug under Linux that could cause an ArgumentOutOfRange crash when
+ searching if only a single result is initially returned.
+
+v0.91
+ Fixed bug where up or down keys would cause an exception if there are no results to scroll through
+
+v0.10
+ Compatibility with KeePass 2.41 (No longer compatible with previous versions)
+
+v0.11
+ Diacritic (accent) insensitive searching
+
+v0.12
+ Removed ugly white top border under Windows 10
+
+v1.0
+ Compatibility with KeePass 2.42. For versions of KeePass prior to 2.42, use an 0.X version.
+
+Attributions
+------------
+Throbber image by FlipDarius http://www.mediawiki.org/wiki/File:Loading.gif
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..54be39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Releases/*
+!Releases/PackageRelease.bat
diff --git a/AutoTypeSearch/.gitignore b/AutoTypeSearch/.gitignore
new file mode 100644
index 0000000..114a799
--- /dev/null
+++ b/AutoTypeSearch/.gitignore
@@ -0,0 +1,357 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*[.json, .xml, .info]
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/AutoTypeSearch/Actions.cs b/AutoTypeSearch/Actions.cs
new file mode 100755
index 0000000..096c515
--- /dev/null
+++ b/AutoTypeSearch/Actions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+
+namespace AutoTypeSearch
+{
+ internal enum Actions
+ {
+ PerformAutoType,
+ EditEntry,
+ ShowEntry,
+ OpenEntryUrl,
+ CopyPassword
+ }
+}
diff --git a/AutoTypeSearch/AutoTypeSearch.csproj b/AutoTypeSearch/AutoTypeSearch.csproj
new file mode 100755
index 0000000..7be4bdd
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.csproj
@@ -0,0 +1,127 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}
+ Library
+ Properties
+ AutoTypeSearch
+ AutoTypeSearch
+ v4.6.1
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\KeePass-Source\Build\KeePass\Debug\Plugins\AutoTypeSearch\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ false
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}
+ KeePass
+ False
+
+
+
+
+
+
+ ..\..\KeePass\KeePass.exe
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ Options.cs
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ Form
+
+
+ SearchWindow.cs
+
+
+
+
+ Options.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ SearchWindow.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ IF $(ConfigurationName) == Release "$(ProjectDir)..\CreatePlgX.bat"
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/AutoTypeSearch.sln b/AutoTypeSearch/AutoTypeSearch.sln
new file mode 100755
index 0000000..5812d0e
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearch.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTypeSearch", "AutoTypeSearch.csproj", "{CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePass", "..\..\KeePass-Source\KeePass\KeePass.csproj", "{10938016-DEE2-4A25-9A5A-8FD3444379CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{93BF1946-D769-4387-B47C-6269FBCE2303}"
+ ProjectSection(SolutionItems) = preProject
+ ..\Releases\PackageRelease.bat = ..\Releases\PackageRelease.bat
+ ..\Readme.txt = ..\Readme.txt
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD1AD3A1-EEEB-47C5-BB7E-43B59BDC9635}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10938016-DEE2-4A25-9A5A-8FD3444379CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoTypeSearch/AutoTypeSearchExt.cs b/AutoTypeSearch/AutoTypeSearchExt.cs
new file mode 100755
index 0000000..850bcd6
--- /dev/null
+++ b/AutoTypeSearch/AutoTypeSearchExt.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Security;
+
+namespace AutoTypeSearch
+{
+// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
+ public sealed class AutoTypeSearchExt : Plugin
+ {
+ private const string IpcEventName = "AutoTypeSearch";
+ private const int UnixAutoTypeWaitTime = 500; // Milliseconds
+ internal const string TagsVirtualFieldName = "***TAGS***";
+
+ private IPluginHost mHost;
+ private bool mAutoTypeSuccessful;
+ private string mLastAutoTypeWindowTitle;
+
+ public override string UpdateUrl
+ {
+ get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
+ }
+
+ public override bool Initialize(IPluginHost host)
+ {
+ mHost = host;
+
+ IpcUtilEx.IpcEvent += OnIpcEvent;
+ GlobalWindowManager.WindowAdded += OnWindowAdded;
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ }
+ AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;
+
+ Options.LoadSettings(host);
+
+ return true;
+ }
+
+ #region Unsuccessful AutoType Detection
+ private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
+ {
+ // An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
+ // (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ mAutoTypeSuccessful = false;
+ mLastAutoTypeWindowTitle = e.TargetWindowTitle;
+ AutoType.FilterCompilePre += OnAutoType;
+
+ if (KeePassLib.Native.NativeLib.IsUnix())
+ {
+ // If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
+ // Instead, just wait half a second and hope for the best.
+ var timer = new Timer { Interval = UnixAutoTypeWaitTime };
+ timer.Tick += delegate
+ {
+ timer.Stop();
+ timer.Dispose();
+ OnAutoTypeEnd();
+ };
+ timer.Start();
+ }
+ else
+ {
+ mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
+ }
+ }
+ }
+
+ private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
+ {
+ // Detach event, we are only interested in a single invocation.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ mAutoTypeSuccessful = true;
+ }
+
+ private void OnAutoTypeEnd()
+ {
+ // Detach event, the auto-type failed, it won't be received now.
+ AutoType.FilterCompilePre -= OnAutoType;
+
+ if (!mAutoTypeSuccessful)
+ {
+ ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
+ }
+ }
+ #endregion
+
+ #region Options
+ private void OnWindowAdded(object sender, GwmWindowEventArgs e)
+ {
+ var optionsForm = e.Form as OptionsForm;
+ if (optionsForm != null)
+ {
+ Options.AddToWindow(optionsForm);
+ return;
+ }
+
+ if (Settings.Default.ShowOnFailedAutoType)
+ {
+ var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
+ if (autoTypeCtxForm != null)
+ {
+ mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
+ autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
+ }
+ }
+ }
+
+ private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
+ {
+ var autoTypeCtxForm = (AutoTypeCtxForm)sender;
+ autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;
+
+ if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
+ {
+ ShowSearch();
+ }
+ }
+ #endregion
+
+ public override void Terminate()
+ {
+ IpcUtilEx.IpcEvent -= OnIpcEvent;
+ GlobalWindowManager.WindowAdded -= OnWindowAdded;
+
+ if (!KeePassLib.Native.NativeLib.IsUnix())
+ {
+ HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
+ Options.UnregisterHotKey();
+ }
+
+ Options.SaveSettings(mHost);
+
+ base.Terminate();
+ }
+
+ #region Search Initiation
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ /*
+ var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
+ for (int i = 0; i < 10000; i++)
+ {
+ var pwEntry = new PwEntry(true, true);
+ pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
+ pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
+ pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
+ pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
+ testGroup.AddEntry(pwEntry, true);
+ }*/
+
+ ShowSearch();
+ }
+
+ private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
+ {
+ if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
+ }
+ }
+
+ private void ShowSearch()
+ {
+ ShowSearch(null);
+ }
+
+ private void ShowSearch(string infoText)
+ {
+ // Unlock, if required
+ mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);
+
+
+ if (mHost.MainWindow.IsAtLeastOneFileOpen())
+ {
+ var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
+ searchWindow.Show();
+ searchWindow.Activate();
+ }
+ }
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/HotKeyManager.cs b/AutoTypeSearch/HotKeyManager.cs
new file mode 100755
index 0000000..b33f84b
--- /dev/null
+++ b/AutoTypeSearch/HotKeyManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ // This class taken from: http://stackoverflow.com/questions/3568513/how-to-create-keyboard-shortcut-in-windows-that-call-function-in-my-app/3569097#3569097
+ // And tweaked with answers in: http://stackoverflow.com/questions/15434505/key-capture-using-global-hotkey-in-c-sharp
+ // And logic from KeePass HotKeyManager
+ internal static class HotKeyManager
+ {
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys keys)
+ {
+ int id = System.Threading.Interlocked.Increment(ref _id);
+
+ KeyModifiers modifiers = 0;
+ if ((keys & Keys.Shift) != Keys.None) modifiers |= KeyModifiers.Shift;
+ if ((keys & Keys.Alt) != Keys.None) modifiers |= KeyModifiers.Alt;
+ if ((keys & Keys.Control) != Keys.None) modifiers |= KeyModifiers.Control;
+
+ RegisterHotKey(_wnd.Handle, id, (uint)modifiers, (uint)(keys & Keys.KeyCode));
+ return id;
+ }
+
+ public static bool UnregisterHotKey(int id)
+ {
+ return UnregisterHotKey(_wnd.Handle, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyManager.HotKeyPressed != null)
+ {
+ HotKeyManager.HotKeyPressed(null, e);
+ }
+ }
+
+ private static MessageWindow _wnd = new MessageWindow();
+
+ private class MessageWindow : NativeWindow, IDisposable
+ {
+ public MessageWindow()
+ {
+ CreateHandle(new CreateParams());
+ }
+
+ public void Dispose()
+ {
+ DestroyHandle();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ HotKeyEventArgs e = new HotKeyEventArgs(m.LParam);
+ HotKeyManager.OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ private const int WM_HOTKEY = 0x312;
+ }
+
+ [DllImport("user32")]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32")]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly KeyModifiers Modifiers;
+
+ public HotKeyEventArgs(Keys key, KeyModifiers modifiers)
+ {
+ this.Key = key;
+ this.Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ uint param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (KeyModifiers)(param & 0x0000ffff);
+ }
+ }
+
+ [Flags]
+ public enum KeyModifiers
+ {
+ Alt = 1,
+ Control = 2,
+ Shift = 4,
+ Windows = 8,
+ NoRepeat = 0x4000
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/Info.png b/AutoTypeSearch/Info.png
new file mode 100755
index 0000000..c1a5608
--- /dev/null
+++ b/AutoTypeSearch/Info.png
Binary files differ
diff --git a/AutoTypeSearch/NativeMethods.cs b/AutoTypeSearch/NativeMethods.cs
new file mode 100755
index 0000000..0037441
--- /dev/null
+++ b/AutoTypeSearch/NativeMethods.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using KeePassLib.Native;
+using Microsoft.Win32;
+
+namespace AutoTypeSearch
+{
+ internal static class NativeMethods
+ {
+ private const int EM_SETMARGINS = 0x00D3;
+ private const int EC_RIGHTMARGIN = 0x2;
+
+ private const int WM_NCLBUTTONDOWN = 0xA1;
+ private const int HTCAPTION = 0x2;
+ [DllImport("User32.dll")]
+ private static extern bool ReleaseCapture();
+ [DllImport("User32.dll")]
+ private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+
+ private const int SWP_NOSIZE = 0x0001;
+ private const int SWP_NOMOVE = 0x0002;
+ private const int SWP_NOZORDER = 0x0004;
+ private const int SWP_FRAMECHANGED = 0x0020;
+ [DllImport("user32.dll", SetLastError=true)]
+ private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
+
+ private const int WM_NCCALCSIZE = 0x83;
+
+ private struct RECT
+ {
+ public int Left, Top, Right, Bottom;
+ }
+ private struct WINDOWPOS
+ {
+ public IntPtr hwnd;
+ public IntPtr hwndinsertafter;
+ public int x, y, cx, cy;
+ public int flags;
+ }
+
+ struct NCCALCSIZE_PARAMS
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public RECT[] rgrc;
+ public WINDOWPOS lppos;
+ }
+
+ public static void SetTextBoxRightMargin(TextBox control, int rightMargin)
+ {
+ SendMessage(control.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin << 16);
+ }
+
+ public static void StartFormDrag(Form form)
+ {
+ Debug.Assert(Control.MouseButtons == MouseButtons.Left);
+ ReleaseCapture();
+ SendMessage(form.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ }
+
+ public static void RefreshWindowFrame(IntPtr hWnd)
+ {
+ NativeMethods.SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+ }
+
+ public static void RemoveWindowFrameTopBorder(ref Message m, int borderHeight)
+ {
+ if (m.Msg == WM_NCCALCSIZE)
+ {
+ var csp = (NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
+ csp.rgrc[0].Top -= borderHeight;
+ Marshal.StructureToPtr(csp, m.LParam, false);
+ }
+ }
+
+ public static bool IsWindows10()
+ {
+ return NativeLib.GetPlatformID() == PlatformID.Win32NT &&
+ // Can't just use OS Version because Windows 10 lies if you don't have specific support declared in the manifest.
+ (int)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", -1) == 10;
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.Designer.cs b/AutoTypeSearch/Options.Designer.cs
new file mode 100755
index 0000000..4886b6d
--- /dev/null
+++ b/AutoTypeSearch/Options.Designer.cs
@@ -0,0 +1,324 @@
+using KeePass.UI;
+
+namespace AutoTypeSearch
+{
+ partial class Options
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.GroupBox searchOptionsGroup;
+ System.Windows.Forms.GroupBox searchInGroup;
+ System.Windows.Forms.GroupBox actionsGroup;
+ System.Windows.Forms.Label alternativeActionLabel;
+ System.Windows.Forms.Label defaultActionLabel;
+ this.mResolveReferences = new System.Windows.Forms.CheckBox();
+ this.mExcludeExpired = new System.Windows.Forms.CheckBox();
+ this.mCaseSensitive = new System.Windows.Forms.CheckBox();
+ this.mSearchInTags = new System.Windows.Forms.CheckBox();
+ this.mSearchInOtherFields = new System.Windows.Forms.CheckBox();
+ this.mSearchInNotes = new System.Windows.Forms.CheckBox();
+ this.mSearchInUrl = new System.Windows.Forms.CheckBox();
+ this.mSearchInUserName = new System.Windows.Forms.CheckBox();
+ this.mSearchInTitle = new System.Windows.Forms.CheckBox();
+ this.mAlternativeAction = new System.Windows.Forms.ComboBox();
+ this.mDefaultAction = new System.Windows.Forms.ComboBox();
+ this.mShowHotKeyControl = new KeePass.UI.HotKeyControlEx();
+ this.mShowSearchGroup = new System.Windows.Forms.GroupBox();
+ this.mShowOnHotKey = new System.Windows.Forms.CheckBox();
+ this.mShowOnIPC = new System.Windows.Forms.CheckBox();
+ this.mShowOnFailedSearch = new System.Windows.Forms.CheckBox();
+ searchOptionsGroup = new System.Windows.Forms.GroupBox();
+ searchInGroup = new System.Windows.Forms.GroupBox();
+ actionsGroup = new System.Windows.Forms.GroupBox();
+ alternativeActionLabel = new System.Windows.Forms.Label();
+ defaultActionLabel = new System.Windows.Forms.Label();
+ searchOptionsGroup.SuspendLayout();
+ searchInGroup.SuspendLayout();
+ actionsGroup.SuspendLayout();
+ this.mShowSearchGroup.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // searchOptionsGroup
+ //
+ searchOptionsGroup.Controls.Add(this.mResolveReferences);
+ searchOptionsGroup.Controls.Add(this.mExcludeExpired);
+ searchOptionsGroup.Controls.Add(this.mCaseSensitive);
+ searchOptionsGroup.Location = new System.Drawing.Point(6, 189);
+ searchOptionsGroup.Name = "searchOptionsGroup";
+ searchOptionsGroup.Size = new System.Drawing.Size(540, 45);
+ searchOptionsGroup.TabIndex = 2;
+ searchOptionsGroup.TabStop = false;
+ searchOptionsGroup.Text = "Search options";
+ //
+ // mResolveReferences
+ //
+ this.mResolveReferences.AutoSize = true;
+ this.mResolveReferences.Location = new System.Drawing.Point(251, 20);
+ this.mResolveReferences.Name = "mResolveReferences";
+ this.mResolveReferences.Size = new System.Drawing.Size(170, 17);
+ this.mResolveReferences.TabIndex = 2;
+ this.mResolveReferences.Text = "Resolve fiel&d references (slow)";
+ this.mResolveReferences.UseVisualStyleBackColor = true;
+ //
+ // mExcludeExpired
+ //
+ this.mExcludeExpired.AutoSize = true;
+ this.mExcludeExpired.Location = new System.Drawing.Point(108, 20);
+ this.mExcludeExpired.Name = "mExcludeExpired";
+ this.mExcludeExpired.Size = new System.Drawing.Size(135, 17);
+ this.mExcludeExpired.TabIndex = 1;
+ this.mExcludeExpired.Text = "Exclude &expired entries";
+ this.mExcludeExpired.UseVisualStyleBackColor = true;
+ //
+ // mCaseSensitive
+ //
+ this.mCaseSensitive.AutoSize = true;
+ this.mCaseSensitive.Location = new System.Drawing.Point(10, 20);
+ this.mCaseSensitive.Name = "mCaseSensitive";
+ this.mCaseSensitive.Size = new System.Drawing.Size(94, 17);
+ this.mCaseSensitive.TabIndex = 0;
+ this.mCaseSensitive.Text = "Case-sensiti&ve";
+ this.mCaseSensitive.UseVisualStyleBackColor = true;
+ //
+ // searchInGroup
+ //
+ searchInGroup.Controls.Add(this.mSearchInTags);
+ searchInGroup.Controls.Add(this.mSearchInOtherFields);
+ searchInGroup.Controls.Add(this.mSearchInNotes);
+ searchInGroup.Controls.Add(this.mSearchInUrl);
+ searchInGroup.Controls.Add(this.mSearchInUserName);
+ searchInGroup.Controls.Add(this.mSearchInTitle);
+ searchInGroup.Location = new System.Drawing.Point(6, 136);
+ searchInGroup.Name = "searchInGroup";
+ searchInGroup.Size = new System.Drawing.Size(540, 47);
+ searchInGroup.TabIndex = 1;
+ searchInGroup.TabStop = false;
+ searchInGroup.Text = "Search in";
+ //
+ // mSearchInTags
+ //
+ this.mSearchInTags.AutoSize = true;
+ this.mSearchInTags.Location = new System.Drawing.Point(258, 19);
+ this.mSearchInTags.Name = "mSearchInTags";
+ this.mSearchInTags.Size = new System.Drawing.Size(50, 17);
+ this.mSearchInTags.TabIndex = 4;
+ this.mSearchInTags.Text = "Ta&gs";
+ this.mSearchInTags.UseVisualStyleBackColor = true;
+ //
+ // mSearchInOtherFields
+ //
+ this.mSearchInOtherFields.AutoSize = true;
+ this.mSearchInOtherFields.Location = new System.Drawing.Point(314, 19);
+ this.mSearchInOtherFields.Name = "mSearchInOtherFields";
+ this.mSearchInOtherFields.Size = new System.Drawing.Size(139, 17);
+ this.mSearchInOtherFields.TabIndex = 5;
+ this.mSearchInOtherFields.Text = "&Other unprotected fields";
+ this.mSearchInOtherFields.UseVisualStyleBackColor = true;
+ //
+ // mSearchInNotes
+ //
+ this.mSearchInNotes.AutoSize = true;
+ this.mSearchInNotes.Location = new System.Drawing.Point(198, 19);
+ this.mSearchInNotes.Name = "mSearchInNotes";
+ this.mSearchInNotes.Size = new System.Drawing.Size(54, 17);
+ this.mSearchInNotes.TabIndex = 3;
+ this.mSearchInNotes.Text = "Note&s";
+ this.mSearchInNotes.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUrl
+ //
+ this.mSearchInUrl.AutoSize = true;
+ this.mSearchInUrl.Location = new System.Drawing.Point(144, 19);
+ this.mSearchInUrl.Name = "mSearchInUrl";
+ this.mSearchInUrl.Size = new System.Drawing.Size(48, 17);
+ this.mSearchInUrl.TabIndex = 2;
+ this.mSearchInUrl.Text = "&URL";
+ this.mSearchInUrl.UseVisualStyleBackColor = true;
+ //
+ // mSearchInUserName
+ //
+ this.mSearchInUserName.AutoSize = true;
+ this.mSearchInUserName.Location = new System.Drawing.Point(61, 19);
+ this.mSearchInUserName.Name = "mSearchInUserName";
+ this.mSearchInUserName.Size = new System.Drawing.Size(77, 17);
+ this.mSearchInUserName.TabIndex = 1;
+ this.mSearchInUserName.Text = "User &name";
+ this.mSearchInUserName.UseVisualStyleBackColor = true;
+ //
+ // mSearchInTitle
+ //
+ this.mSearchInTitle.AutoSize = true;
+ this.mSearchInTitle.Location = new System.Drawing.Point(9, 19);
+ this.mSearchInTitle.Name = "mSearchInTitle";
+ this.mSearchInTitle.Size = new System.Drawing.Size(46, 17);
+ this.mSearchInTitle.TabIndex = 0;
+ this.mSearchInTitle.Text = "&Title";
+ this.mSearchInTitle.UseVisualStyleBackColor = true;
+ //
+ // actionsGroup
+ //
+ actionsGroup.Controls.Add(this.mAlternativeAction);
+ actionsGroup.Controls.Add(this.mDefaultAction);
+ actionsGroup.Controls.Add(alternativeActionLabel);
+ actionsGroup.Controls.Add(defaultActionLabel);
+ actionsGroup.Location = new System.Drawing.Point(6, 241);
+ actionsGroup.Name = "actionsGroup";
+ actionsGroup.Size = new System.Drawing.Size(540, 67);
+ actionsGroup.TabIndex = 3;
+ actionsGroup.TabStop = false;
+ actionsGroup.Text = "Actions";
+ //
+ // mAlternativeAction
+ //
+ this.mAlternativeAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mAlternativeAction.Location = new System.Drawing.Point(288, 37);
+ this.mAlternativeAction.Name = "mAlternativeAction";
+ this.mAlternativeAction.Size = new System.Drawing.Size(240, 21);
+ this.mAlternativeAction.TabIndex = 3;
+ //
+ // mDefaultAction
+ //
+ this.mDefaultAction.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.mDefaultAction.Location = new System.Drawing.Point(11, 37);
+ this.mDefaultAction.Name = "mDefaultAction";
+ this.mDefaultAction.Size = new System.Drawing.Size(240, 21);
+ this.mDefaultAction.TabIndex = 1;
+ //
+ // alternativeActionLabel
+ //
+ alternativeActionLabel.AutoSize = true;
+ alternativeActionLabel.Location = new System.Drawing.Point(285, 20);
+ alternativeActionLabel.Name = "alternativeActionLabel";
+ alternativeActionLabel.Size = new System.Drawing.Size(159, 13);
+ alternativeActionLabel.TabIndex = 2;
+ alternativeActionLabel.Text = "A<ernative action (Shift + Enter):";
+ //
+ // defaultActionLabel
+ //
+ defaultActionLabel.AutoSize = true;
+ defaultActionLabel.Location = new System.Drawing.Point(8, 20);
+ defaultActionLabel.Name = "defaultActionLabel";
+ defaultActionLabel.Size = new System.Drawing.Size(110, 13);
+ defaultActionLabel.TabIndex = 0;
+ defaultActionLabel.Text = "De&fault action (Enter):";
+ //
+ // mShowHotKeyControl
+ //
+ this.mShowHotKeyControl.Location = new System.Drawing.Point(30, 65);
+ this.mShowHotKeyControl.Name = "mShowHotKeyControl";
+ this.mShowHotKeyControl.Size = new System.Drawing.Size(123, 20);
+ this.mShowHotKeyControl.TabIndex = 2;
+ //
+ // mShowSearchGroup
+ //
+ this.mShowSearchGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mShowSearchGroup.Controls.Add(this.mShowOnHotKey);
+ this.mShowSearchGroup.Controls.Add(this.mShowHotKeyControl);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnIPC);
+ this.mShowSearchGroup.Controls.Add(this.mShowOnFailedSearch);
+ this.mShowSearchGroup.Location = new System.Drawing.Point(6, 12);
+ this.mShowSearchGroup.Name = "mShowSearchGroup";
+ this.mShowSearchGroup.Size = new System.Drawing.Size(540, 118);
+ this.mShowSearchGroup.TabIndex = 0;
+ this.mShowSearchGroup.TabStop = false;
+ this.mShowSearchGroup.Text = "Show search window";
+ //
+ // mShowOnHotKey
+ //
+ this.mShowOnHotKey.AutoSize = true;
+ this.mShowOnHotKey.Location = new System.Drawing.Point(10, 44);
+ this.mShowOnHotKey.Name = "mShowOnHotKey";
+ this.mShowOnHotKey.Size = new System.Drawing.Size(233, 17);
+ this.mShowOnHotKey.TabIndex = 1;
+ this.mShowOnHotKey.Text = "Show when system-wide &hot key is pressed:";
+ this.mShowOnHotKey.UseVisualStyleBackColor = true;
+ this.mShowOnHotKey.CheckedChanged += new System.EventHandler(this.mShowOnHotKey_CheckedChanged);
+ //
+ // mShowOnIPC
+ //
+ this.mShowOnIPC.AutoSize = true;
+ this.mShowOnIPC.Location = new System.Drawing.Point(10, 93);
+ this.mShowOnIPC.Name = "mShowOnIPC";
+ this.mShowOnIPC.Size = new System.Drawing.Size(386, 17);
+ this.mShowOnIPC.TabIndex = 3;
+ this.mShowOnIPC.Text = "Show when \"/e1:AutoTypeSearch\" is passed as a ¶meter to KeePass.exe";
+ this.mShowOnIPC.UseVisualStyleBackColor = true;
+ //
+ // mShowOnFailedSearch
+ //
+ this.mShowOnFailedSearch.AutoSize = true;
+ this.mShowOnFailedSearch.Location = new System.Drawing.Point(10, 21);
+ this.mShowOnFailedSearch.Name = "mShowOnFailedSearch";
+ this.mShowOnFailedSearch.Size = new System.Drawing.Size(275, 17);
+ this.mShowOnFailedSearch.TabIndex = 0;
+ this.mShowOnFailedSearch.Text = "Show &automatically if global auto-type finds no match";
+ this.mShowOnFailedSearch.UseVisualStyleBackColor = true;
+ //
+ // Options
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(actionsGroup);
+ this.Controls.Add(searchInGroup);
+ this.Controls.Add(searchOptionsGroup);
+ this.Controls.Add(this.mShowSearchGroup);
+ this.Name = "Options";
+ this.Size = new System.Drawing.Size(551, 311);
+ searchOptionsGroup.ResumeLayout(false);
+ searchOptionsGroup.PerformLayout();
+ searchInGroup.ResumeLayout(false);
+ searchInGroup.PerformLayout();
+ actionsGroup.ResumeLayout(false);
+ actionsGroup.PerformLayout();
+ this.mShowSearchGroup.ResumeLayout(false);
+ this.mShowSearchGroup.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private KeePass.UI.HotKeyControlEx mShowHotKeyControl;
+ private System.Windows.Forms.CheckBox mShowOnHotKey;
+ private System.Windows.Forms.CheckBox mShowOnIPC;
+ private System.Windows.Forms.CheckBox mShowOnFailedSearch;
+ private System.Windows.Forms.CheckBox mCaseSensitive;
+ private System.Windows.Forms.CheckBox mSearchInTags;
+ private System.Windows.Forms.CheckBox mSearchInOtherFields;
+ private System.Windows.Forms.CheckBox mSearchInNotes;
+ private System.Windows.Forms.CheckBox mSearchInUrl;
+ private System.Windows.Forms.CheckBox mSearchInUserName;
+ private System.Windows.Forms.CheckBox mSearchInTitle;
+ private System.Windows.Forms.CheckBox mResolveReferences;
+ private System.Windows.Forms.CheckBox mExcludeExpired;
+ private System.Windows.Forms.ComboBox mAlternativeAction;
+ private System.Windows.Forms.ComboBox mDefaultAction;
+ private System.Windows.Forms.GroupBox mShowSearchGroup;
+
+ }
+}
diff --git a/AutoTypeSearch/Options.cs b/AutoTypeSearch/Options.cs
new file mode 100755
index 0000000..b99561c
--- /dev/null
+++ b/AutoTypeSearch/Options.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Plugins;
+using KeePassLib;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ internal partial class Options : UserControl
+ {
+ private const string OptionsConfigRoot = "AutoTypeSearchExt.";
+
+ private static int sRegisteredHotkeyId;
+
+ // ReSharper disable once MemberCanBePrivate.Global - Public for forms designer
+ public Options()
+ {
+ InitializeComponent();
+
+ // Must mach order and values of Actions enum
+ var actions = new object[] { Resources.PerformAutoType, Resources.EditEntry, Resources.ShowEntry, Resources.OpenEntryUrl, Resources.CopyPassword };
+ mDefaultAction.Items.AddRange(actions);
+ mAlternativeAction.Items.AddRange(actions);
+
+ // Read options
+ mShowOnFailedSearch.Checked = Settings.Default.ShowOnFailedAutoType;
+
+ if (NativeLib.IsUnix())
+ {
+ mShowOnHotKey.Enabled = false;
+ mShowOnHotKey.Checked = false;
+
+ mShowHotKeyControl.Clear();
+ }
+ else
+ {
+ mShowOnHotKey.Checked = Settings.Default.ShowOnHotKey;
+ ShowHotKey = Settings.Default.ShowHotKey;
+ }
+ mShowOnHotKey_CheckedChanged(null, EventArgs.Empty);
+
+ mShowOnIPC.Checked = Settings.Default.ShowOnIPC;
+ mSearchInTitle.Checked = Settings.Default.SearchTitle;
+ mSearchInUserName.Checked = Settings.Default.SearchUserName;
+ mSearchInUrl.Checked = Settings.Default.SearchUrl;
+ mSearchInNotes.Checked = Settings.Default.SearchNotes;
+ mSearchInTags.Checked = Settings.Default.SearchTags;
+ mSearchInOtherFields.Checked = Settings.Default.SearchCustomFields;
+
+ mCaseSensitive.Checked = Settings.Default.CaseSensitive;
+ mExcludeExpired.Checked = Settings.Default.ExcludeExpired;
+ mResolveReferences.Checked = Settings.Default.ResolveReferences;
+
+ mDefaultAction.SelectedIndex = (int)Settings.Default.DefaultAction;
+ mAlternativeAction.SelectedIndex = (int)Settings.Default.AlternativeAction;
+ }
+
+ private Keys ShowHotKey
+ {
+ get { return mShowHotKeyControl.HotKey; }
+ set { mShowHotKeyControl.HotKey = value; }
+ }
+
+ private void mShowOnHotKey_CheckedChanged(object sender, EventArgs e)
+ {
+ mShowHotKeyControl.Enabled = mShowOnHotKey.Checked;
+ }
+
+ private void ApplySettings()
+ {
+ // Apply settings
+ Settings.Default.ShowOnFailedAutoType = mShowOnFailedSearch.Checked;
+ Settings.Default.ShowOnHotKey = mShowOnHotKey.Checked;
+ Settings.Default.ShowOnIPC = mShowOnIPC.Checked;
+ Settings.Default.SearchTitle = mSearchInTitle.Checked;
+ Settings.Default.SearchUserName = mSearchInUserName.Checked;
+ Settings.Default.SearchUrl = mSearchInUrl.Checked;
+ Settings.Default.SearchNotes = mSearchInNotes.Checked;
+ Settings.Default.SearchTags = mSearchInTags.Checked;
+ Settings.Default.SearchCustomFields = mSearchInOtherFields.Checked;
+ Settings.Default.CaseSensitive = mCaseSensitive.Checked;
+ Settings.Default.ExcludeExpired = mExcludeExpired.Checked;
+ Settings.Default.ResolveReferences = mResolveReferences.Checked;
+ Settings.Default.DefaultAction = (Actions)mDefaultAction.SelectedIndex;
+ Settings.Default.AlternativeAction = (Actions)mAlternativeAction.SelectedIndex;
+ Settings.Default.ShowHotKey = ShowHotKey;
+
+ ApplyHotKey();
+ }
+
+ #region Settings persistence
+ public static void SaveSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ if (property.IsDirty)
+ {
+ var value = property.SerializedValue as String;
+ if (value != null)
+ {
+ host.CustomConfig.SetString(OptionsConfigRoot + property.Name, value);
+ }
+ else
+ {
+ Debug.Fail("Non-string serialized settings property");
+ }
+ }
+ }
+ }
+ }
+
+ public static void LoadSettings(IPluginHost host)
+ {
+ if (host != null)
+ {
+ // ReSharper disable once UnusedVariable
+ var ignored = Settings.Default.ShowOnFailedAutoType; //Access any property just to make it load settings.
+
+ foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
+ {
+ var value = host.CustomConfig.GetString(OptionsConfigRoot + property.Name);
+ if (value != null)
+ {
+ property.SerializedValue = value;
+ property.Deserialized = false;
+ property.IsDirty = false;
+ }
+ }
+
+ ApplyHotKey();
+ }
+ }
+ #endregion
+
+ #region Hotkey
+ private static void ApplyHotKey()
+ {
+ UnregisterHotKey();
+
+ if (Settings.Default.ShowOnHotKey && Settings.Default.ShowHotKey != Keys.None)
+ {
+ sRegisteredHotkeyId = HotKeyManager.RegisterHotKey(Settings.Default.ShowHotKey);
+ }
+ }
+
+ public static void UnregisterHotKey()
+ {
+ if (sRegisteredHotkeyId != 0)
+ {
+ var result = HotKeyManager.UnregisterHotKey(sRegisteredHotkeyId);
+ Debug.Assert(result);
+ sRegisteredHotkeyId = 0;
+ }
+ }
+ #endregion
+
+ public static void AddToWindow(OptionsForm optionsForm)
+ {
+ var tabControl = optionsForm.Controls.Find("m_tabMain", false).FirstOrDefault() as TabControl;
+ var okButton = optionsForm.Controls.Find("m_btnOK", false).FirstOrDefault() as Button;
+
+ if (tabControl == null || okButton == null)
+ {
+ Debug.Fail("Could not integrate with options form");
+ }
+
+ var tabPage = new TabPage(Resources.AutoTypeSearch)
+ {
+ UseVisualStyleBackColor = true,
+ AutoScroll = true,
+ ImageIndex = (int)PwIcon.EMailSearch
+ };
+ var options = new Options { Dock = DockStyle.Fill };
+ tabPage.Controls.Add(options);
+
+ tabControl.TabPages.Add(tabPage);
+
+ okButton.Click += delegate
+ {
+ options.ApplySettings();
+ };
+ }
+ }
+}
diff --git a/AutoTypeSearch/Options.resx b/AutoTypeSearch/Options.resx
new file mode 100755
index 0000000..4601c27
--- /dev/null
+++ b/AutoTypeSearch/Options.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/AssemblyInfo.cs b/AutoTypeSearch/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..4a8b0ac
--- /dev/null
+++ b/AutoTypeSearch/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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: AssemblyTitle("AutoTypeSearch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Alex Vallat")]
+[assembly: AssemblyProduct("KeePass Plugin")]
+[assembly: AssemblyCopyright("Copyright © 2017 Alex Vallat")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c4effc53-d77b-45e0-9d11-a0b9661ae822")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.42.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoTypeSearch/Properties/Resources.Designer.cs b/AutoTypeSearch/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..4a4fbaf
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.Designer.cs
@@ -0,0 +1,145 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AutoTypeSearch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Global auto-type found no match for window: "{0}".
+ ///
+ internal static string AutoTypeFailedMessage {
+ get {
+ return ResourceManager.GetString("AutoTypeFailedMessage", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to AutoTypeSearch.
+ ///
+ internal static string AutoTypeSearch {
+ get {
+ return ResourceManager.GetString("AutoTypeSearch", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start typing to search entries.
+ ///
+ internal static string BannerText {
+ get {
+ return ResourceManager.GetString("BannerText", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Copy password.
+ ///
+ internal static string CopyPassword {
+ get {
+ return ResourceManager.GetString("CopyPassword", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Edit entry.
+ ///
+ internal static string EditEntry {
+ get {
+ return ResourceManager.GetString("EditEntry", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Info {
+ get {
+ object obj = ResourceManager.GetObject("Info", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Open entry url.
+ ///
+ internal static string OpenEntryUrl {
+ get {
+ return ResourceManager.GetString("OpenEntryUrl", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Perform entry auto-type.
+ ///
+ internal static string PerformAutoType {
+ get {
+ return ResourceManager.GetString("PerformAutoType", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show entry in the main window.
+ ///
+ internal static string ShowEntry {
+ get {
+ return ResourceManager.GetString("ShowEntry", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Resources.resx b/AutoTypeSearch/Properties/Resources.resx
new file mode 100755
index 0000000..76e9bce
--- /dev/null
+++ b/AutoTypeSearch/Properties/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Global auto-type found no match for window: "{0}"
+
+
+ AutoTypeSearch
+
+
+ Start typing to search entries
+
+
+ Copy password
+
+
+ Edit entry
+
+
+
+ ..\Info.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Open entry url
+
+
+ Perform entry auto-type
+
+
+ Show entry in the main window
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Properties/Settings.Designer.cs b/AutoTypeSearch/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..62e2cdb
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoTypeSearch.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTitle {
+ get {
+ return ((bool)(this["SearchTitle"]));
+ }
+ set {
+ this["SearchTitle"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool SearchUserName {
+ get {
+ return ((bool)(this["SearchUserName"]));
+ }
+ set {
+ this["SearchUserName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchUrl {
+ get {
+ return ((bool)(this["SearchUrl"]));
+ }
+ set {
+ this["SearchUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchNotes {
+ get {
+ return ((bool)(this["SearchNotes"]));
+ }
+ set {
+ this["SearchNotes"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchCustomFields {
+ get {
+ return ((bool)(this["SearchCustomFields"]));
+ }
+ set {
+ this["SearchCustomFields"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool SearchTags {
+ get {
+ return ((bool)(this["SearchTags"]));
+ }
+ set {
+ this["SearchTags"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool CaseSensitive {
+ get {
+ return ((bool)(this["CaseSensitive"]));
+ }
+ set {
+ this["CaseSensitive"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0, 0, 0, 0")]
+ public global::System.Drawing.Rectangle WindowPosition {
+ get {
+ return ((global::System.Drawing.Rectangle)(this["WindowPosition"]));
+ }
+ set {
+ this["WindowPosition"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnFailedAutoType {
+ get {
+ return ((bool)(this["ShowOnFailedAutoType"]));
+ }
+ set {
+ this["ShowOnFailedAutoType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ShowOnHotKey {
+ get {
+ return ((bool)(this["ShowOnHotKey"]));
+ }
+ set {
+ this["ShowOnHotKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ShowOnIPC {
+ get {
+ return ((bool)(this["ShowOnIPC"]));
+ }
+ set {
+ this["ShowOnIPC"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ExcludeExpired {
+ get {
+ return ((bool)(this["ExcludeExpired"]));
+ }
+ set {
+ this["ExcludeExpired"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool ResolveReferences {
+ get {
+ return ((bool)(this["ResolveReferences"]));
+ }
+ set {
+ this["ResolveReferences"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PerformAutoType")]
+ public global::AutoTypeSearch.Actions DefaultAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["DefaultAction"]));
+ }
+ set {
+ this["DefaultAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("EditEntry")]
+ public global::AutoTypeSearch.Actions AlternativeAction {
+ get {
+ return ((global::AutoTypeSearch.Actions)(this["AlternativeAction"]));
+ }
+ set {
+ this["AlternativeAction"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("None")]
+ public global::System.Windows.Forms.Keys ShowHotKey {
+ get {
+ return ((global::System.Windows.Forms.Keys)(this["ShowHotKey"]));
+ }
+ set {
+ this["ShowHotKey"] = value;
+ }
+ }
+ }
+}
diff --git a/AutoTypeSearch/Properties/Settings.settings b/AutoTypeSearch/Properties/Settings.settings
new file mode 100755
index 0000000..edcae1b
--- /dev/null
+++ b/AutoTypeSearch/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchResult.cs b/AutoTypeSearch/SearchResult.cs
new file mode 100755
index 0000000..5af4177
--- /dev/null
+++ b/AutoTypeSearch/SearchResult.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResult
+ {
+ private readonly PwDatabase mDatabase;
+ private readonly PwEntry mEntry;
+ private readonly string mFieldName;
+ private readonly int mStart;
+ private readonly int mLength;
+ private readonly string mFieldValue;
+ private readonly string mTitle;
+ private string mUniqueTitlePart;
+ private int mResultIndex = -1;
+
+ public SearchResult(PwDatabase database, PwEntry entry, string title, string fieldName, string fieldValue, int start, int length)
+ {
+ mDatabase = database;
+ mEntry = entry;
+ mFieldName = fieldName;
+ mFieldValue = fieldValue;
+ mStart = start;
+ mLength = length;
+ mTitle = title;
+
+ Debug.Assert(mLength >= 0 && mStart >= 0, "Negative values are invalid");
+ Debug.Assert(mLength > 0 || mStart == 0, "Length must be non-zero (unless no highlight)");
+ Debug.Assert((mStart + mLength) <= fieldValue.Length, "Length out of range");
+ }
+
+ public PwDatabase Database
+ {
+ get { return mDatabase; }
+ }
+
+ public PwEntry Entry
+ {
+ get { return mEntry; }
+ }
+
+ public string FieldName
+ {
+ get { return mFieldName; }
+ }
+
+ public string FieldValue
+ {
+ get { return mFieldValue; }
+ }
+
+ public int Start
+ {
+ get { return mStart; }
+ }
+
+ public int Length
+ {
+ get { return mLength; }
+ }
+
+ public string Title
+ {
+ get { return mTitle; }
+ }
+
+ ///
+ /// The UniqueTitle may be modified from the to ensure uniqueness in the list of results
+ ///
+ public string UniqueTitle
+ {
+ get { return UniqueTitlePart + Title; }
+ }
+
+ public string UniqueTitlePart
+ {
+ get { return mUniqueTitlePart; }
+ }
+
+ public int ResultIndex
+ {
+ get { return mResultIndex; }
+ }
+
+ public void SetResultIndex(int resultIndex)
+ {
+ if (mResultIndex != -1)
+ {
+ throw new InvalidOperationException("Result index has already been set");
+ }
+ if (resultIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException("resultIndex");
+ }
+
+ mResultIndex = resultIndex;
+ }
+
+ ///
+ /// Sets by including parent group names to the specified depth.
+ ///
+ /// True if the group hierarchy is deep enough to support full requested
+ public bool SetUniqueTitleDepth(int depth)
+ {
+ var groupPath = new StringBuilder();
+ var group = Entry.ParentGroup;
+ for (int i = 0; i < depth && group != null; i++)
+ {
+ groupPath.Insert(0, group.Name + " / ");
+ group = group.ParentGroup;
+ }
+
+ mUniqueTitlePart = groupPath.ToString();
+
+ return group != null;
+ }
+
+
+ }
+}
diff --git a/AutoTypeSearch/SearchResults.cs b/AutoTypeSearch/SearchResults.cs
new file mode 100755
index 0000000..b2b0529
--- /dev/null
+++ b/AutoTypeSearch/SearchResults.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePass.Util.Spr;
+using KeePassLib;
+using KeePassLib.Utility;
+
+namespace AutoTypeSearch
+{
+ internal class SearchResults
+ {
+ private readonly string mTerm;
+ private readonly SearchResult[] mResults;
+
+ private readonly object mLock = new object();
+ private volatile int mCount;
+ private volatile bool mComplete;
+
+ private readonly AutoResetEvent mResultsUpdated = new AutoResetEvent(false);
+
+ private readonly CompareOptions mStringComparison;
+ private readonly bool mSearchTitle;
+ private readonly bool mSearchUserName;
+ private readonly bool mSearchUrl;
+ private readonly bool mSearchNotes;
+ private readonly bool mSearchCustomFields;
+ private readonly bool mResolveReferences;
+ private readonly bool mSearchTags;
+
+ public SearchResults(int capacity, string term)
+ {
+ mTerm = term;
+ mResults = new SearchResult[capacity];
+
+ mStringComparison = Settings.Default.CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase;
+ mStringComparison |= CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreNonSpace;
+ mSearchTitle = Settings.Default.SearchTitle;
+ mSearchUserName = Settings.Default.SearchUserName;
+ mSearchUrl = Settings.Default.SearchUrl;
+ mSearchNotes = Settings.Default.SearchNotes;
+ mSearchCustomFields = Settings.Default.SearchCustomFields;
+ mSearchTags = Settings.Default.SearchTags;
+ mResolveReferences = Settings.Default.ResolveReferences;
+ }
+
+ ///
+ /// Gets an ordered list of fields to search for the term
+ ///
+ ///
+ ///
+ private IEnumerable GetFieldsToSearch(PwEntry entry)
+ {
+ var fieldsToSearch = new List((int)entry.Strings.UCount);
+ if (mSearchTitle) fieldsToSearch.Add(PwDefs.TitleField);
+ if (mSearchUserName) fieldsToSearch.Add(PwDefs.UserNameField);
+ if (mSearchUrl) fieldsToSearch.Add(PwDefs.UrlField);
+ if (mSearchNotes) fieldsToSearch.Add(PwDefs.NotesField);
+ if (mSearchCustomFields)
+ {
+ foreach (var stringEntry in entry.Strings)
+ {
+ if (!stringEntry.Value.IsProtected && !PwDefs.IsStandardField(stringEntry.Key))
+ {
+ fieldsToSearch.Add(stringEntry.Key);
+ }
+ }
+ }
+ if (mSearchTags) fieldsToSearch.Add(AutoTypeSearchExt.TagsVirtualFieldName);
+
+ return fieldsToSearch;
+ }
+
+ public void AddResultIfMatchesTerm(PwDatabase context, PwEntry entry)
+ {
+ // First try without resolving
+ var addedResult = AddResultIfMatchesTerm(context, entry, false);
+
+ if (!addedResult && mResolveReferences)
+ {
+ // Not found without resolving, so try resolving
+ AddResultIfMatchesTerm(context, entry, true);
+ }
+ }
+
+ private bool AddResultIfMatchesTerm(PwDatabase context, PwEntry entry, bool resolveReferences)
+ {
+ foreach (var fieldName in GetFieldsToSearch(entry))
+ {
+ string fieldValue;
+ if (fieldName == AutoTypeSearchExt.TagsVirtualFieldName)
+ {
+ fieldValue = StrUtil.TagsToString(entry.Tags, true);
+ }
+ else
+ {
+ fieldValue = entry.Strings.ReadSafeEx(fieldName);
+
+ if (resolveReferences)
+ {
+ fieldValue = ResolveReferences(context, entry, fieldValue);
+ }
+ }
+
+ if (!String.IsNullOrEmpty(fieldValue))
+ {
+ var foundIndex = CultureInfo.CurrentCulture.CompareInfo.IndexOf(fieldValue, mTerm, mStringComparison);
+ if (foundIndex >= 0)
+ {
+ // Found a match, create a search result and add it
+ AddResult(new SearchResult(context, entry, entry.Strings.ReadSafe(PwDefs.TitleField), fieldName, fieldValue, foundIndex, mTerm.Length));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Resolves any references in the field value and returns it. If there were no references,
+ /// returns null (to avoid duplicate searching - it is assumed that the unresolved value has already been searched)
+ ///
+ private string ResolveReferences(PwDatabase context, PwEntry entry, string fieldValue)
+ {
+ if (fieldValue.IndexOf('{') < 0)
+ {
+ // Can't contain any references
+ return null;
+ }
+
+ var sprContext = new SprContext(entry, context, SprCompileFlags.Deref) { ForcePlainTextPasswords = false };
+
+ var result = SprEngine.Compile(fieldValue, sprContext);
+ if (CultureInfo.CurrentCulture.CompareInfo.Compare(result,fieldValue, mStringComparison) == 0)
+ {
+ return null;
+ }
+
+ return result;
+ }
+
+ public void AddResultIfMatchesTerm(SearchResult candidate)
+ {
+ // First see whether the existing candidate is a further match in the same place
+ var fieldValue = candidate.FieldValue;
+ if (fieldValue.Length > candidate.Start + mTerm.Length && CultureInfo.CurrentCulture.CompareInfo.Compare(fieldValue.Substring(candidate.Start, mTerm.Length), mTerm, mStringComparison) == 0)
+ {
+ // Yep, match continues, so add it.
+ AddResult(new SearchResult(candidate.Database, candidate.Entry, candidate.Title, candidate.FieldName, fieldValue, candidate.Start, mTerm.Length));
+ }
+ else
+ {
+ // Existing candidate match couldn't be extended, so search from scratch again
+ AddResultIfMatchesTerm(candidate.Database, candidate.Entry);
+ }
+ }
+
+ private void AddResult(SearchResult result)
+ {
+ lock (mLock)
+ {
+ if (mComplete)
+ {
+ throw new InvalidOperationException("Search results have been completed");
+ }
+ result.SetResultIndex(mCount);
+ mResults[mCount++] = result;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Indicates that the results are complete, and no more will be added.
+ ///
+ public void SetComplete()
+ {
+ lock (mLock)
+ {
+ mComplete = true;
+ }
+ mResultsUpdated.Set();
+ }
+
+ ///
+ /// Gets all the available results so far.
+ ///
+ /// Index to start returning from. Modified to be the first index not available yet on return.
+ /// Set to true if the results are complete, false if more results are pending but have not been returned.
+ ///
+ public SearchResult[] GetAvailableResults(ref int index, out bool complete)
+ {
+ int count;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (count <= index)
+ {
+ return new SearchResult[0];
+ }
+
+ var availableResults = new SearchResult[count - index];
+ Array.Copy(mResults, index, availableResults, 0, availableResults.Length);
+ index = count;
+
+ return availableResults;
+ }
+
+ ///
+ /// Gets all the results. Will block until complete.
+ ///
+ ///
+ public IEnumerable GetAllResults()
+ {
+ int count = -1;
+
+ for (var i = 0; i < mResults.Length; i++)
+ {
+ if (i > count)
+ {
+ // Reached the limit of availability so far, so see if more is available
+ do
+ {
+ bool moreAvailable, complete;
+
+ lock (mLock)
+ {
+ moreAvailable = mCount > count;
+ count = mCount;
+ complete = mComplete;
+ }
+
+ if (!moreAvailable)
+ {
+ if (complete)
+ {
+ // No more available, but the results are now complete anyway
+ yield break;
+ }
+
+ // No more available yet, not yet complete, wait until more becomes available
+ mResultsUpdated.WaitOne();
+ }
+ else
+ {
+ // More available now, so stop checking for more, continue with the loop to return them
+ break;
+ }
+ } while (true);
+
+ Debug.Assert(i <= count, "More should be available now");
+ }
+
+ yield return mResults[i];
+ }
+ }
+
+ public SearchResults CreateChildResults(string term)
+ {
+ Debug.Assert(term.StartsWith(mTerm));
+
+ int count;
+ bool complete;
+ lock (mLock)
+ {
+ count = mCount;
+ complete = mComplete;
+ }
+
+ // If complete, then we know we don't need more than count. Otherwise, it can't be more than this capacity anyway
+ var childCapacity = complete ? count : mResults.Length;
+
+ return new SearchResults(childCapacity, term);
+ }
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.Designer.cs b/AutoTypeSearch/SearchWindow.Designer.cs
new file mode 100755
index 0000000..18b37d1
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.Designer.cs
@@ -0,0 +1,201 @@
+using System.Windows.Forms;
+
+namespace AutoTypeSearch
+{
+ partial class SearchWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.mSearch = new System.Windows.Forms.TextBox();
+ this.mResults = new System.Windows.Forms.ListBox();
+ this.mLayout = new System.Windows.Forms.TableLayoutPanel();
+ this.mBanner = new System.Windows.Forms.PictureBox();
+ this.mInfoBanner = new System.Windows.Forms.Panel();
+ this.mInfoLabel = new System.Windows.Forms.Label();
+ this.mInfoBannerImage = new System.Windows.Forms.PictureBox();
+ this.mThrobber = new System.Windows.Forms.PictureBox();
+ this.mResultsUpdater = new System.Windows.Forms.Timer(this.components);
+ this.mNoResultsLabel = new System.Windows.Forms.Label();
+ this.mLayout.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).BeginInit();
+ this.mInfoBanner.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).BeginInit();
+ this.SuspendLayout();
+ //
+ // mSearch
+ //
+ this.mSearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.mSearch.Location = new System.Drawing.Point(1, 78);
+ this.mSearch.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0);
+ this.mSearch.Name = "mSearch";
+ this.mSearch.Size = new System.Drawing.Size(521, 20);
+ this.mSearch.TabIndex = 0;
+ this.mSearch.LocationChanged += new System.EventHandler(this.mSearch_LocationChanged);
+ this.mSearch.TextChanged += new System.EventHandler(this.mSearch_TextChanged);
+ //
+ // mResults
+ //
+ this.mResults.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.mResults.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mResults.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
+ this.mResults.FormattingEnabled = true;
+ this.mResults.IntegralHeight = false;
+ this.mResults.Location = new System.Drawing.Point(0, 98);
+ this.mResults.Margin = new System.Windows.Forms.Padding(0);
+ this.mResults.Name = "mResults";
+ this.mResults.Size = new System.Drawing.Size(523, 176);
+ this.mResults.TabIndex = 1;
+ this.mResults.TabStop = false;
+ this.mResults.MouseClick += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseClick);
+ this.mResults.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mResults_DrawItem);
+ this.mResults.LocationChanged += new System.EventHandler(this.mResults_LocationChanged);
+ this.mResults.MouseEnter += new System.EventHandler(this.mResults_MouseEnter);
+ this.mResults.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mResults_MouseMove);
+ //
+ // mLayout
+ //
+ this.mLayout.ColumnCount = 1;
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.mLayout.Controls.Add(this.mSearch, 0, 2);
+ this.mLayout.Controls.Add(this.mResults, 0, 3);
+ this.mLayout.Controls.Add(this.mBanner, 0, 0);
+ this.mLayout.Controls.Add(this.mInfoBanner, 0, 1);
+ this.mLayout.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mLayout.Location = new System.Drawing.Point(0, 0);
+ this.mLayout.Name = "mLayout";
+ this.mLayout.RowCount = 4;
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.mLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.mLayout.Size = new System.Drawing.Size(523, 274);
+ this.mLayout.TabIndex = 2;
+ //
+ // mBanner
+ //
+ this.mBanner.Dock = System.Windows.Forms.DockStyle.Top;
+ this.mBanner.Location = new System.Drawing.Point(0, 0);
+ this.mBanner.Margin = new System.Windows.Forms.Padding(0);
+ this.mBanner.Name = "mBanner";
+ this.mBanner.Size = new System.Drawing.Size(523, 60);
+ this.mBanner.TabIndex = 3;
+ this.mBanner.TabStop = false;
+ this.mBanner.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mBannerImage_MouseDown);
+ //
+ // mInfoBanner
+ //
+ this.mInfoBanner.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.mInfoBanner.BackColor = System.Drawing.SystemColors.Info;
+ this.mInfoBanner.Controls.Add(this.mInfoLabel);
+ this.mInfoBanner.Controls.Add(this.mInfoBannerImage);
+ this.mInfoBanner.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoBanner.Location = new System.Drawing.Point(2, 61);
+ this.mInfoBanner.Margin = new System.Windows.Forms.Padding(2, 1, 1, 1);
+ this.mInfoBanner.Name = "mInfoBanner";
+ this.mInfoBanner.Size = new System.Drawing.Size(520, 16);
+ this.mInfoBanner.TabIndex = 8;
+ //
+ // mInfoLabel
+ //
+ this.mInfoLabel.AutoEllipsis = true;
+ this.mInfoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.mInfoLabel.ForeColor = System.Drawing.SystemColors.InfoText;
+ this.mInfoLabel.Location = new System.Drawing.Point(16, 0);
+ this.mInfoLabel.Name = "mInfoLabel";
+ this.mInfoLabel.Size = new System.Drawing.Size(504, 16);
+ this.mInfoLabel.TabIndex = 6;
+ this.mInfoLabel.Text = "AutoType failed to find";
+ //
+ // mInfoBannerImage
+ //
+ this.mInfoBannerImage.Dock = System.Windows.Forms.DockStyle.Left;
+ this.mInfoBannerImage.Image = global::AutoTypeSearch.Properties.Resources.Info;
+ this.mInfoBannerImage.Location = new System.Drawing.Point(0, 0);
+ this.mInfoBannerImage.Margin = new System.Windows.Forms.Padding(0);
+ this.mInfoBannerImage.Name = "mInfoBannerImage";
+ this.mInfoBannerImage.Size = new System.Drawing.Size(16, 16);
+ this.mInfoBannerImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mInfoBannerImage.TabIndex = 7;
+ this.mInfoBannerImage.TabStop = false;
+ //
+ // mThrobber
+ //
+ this.mThrobber.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.mThrobber.BackColor = System.Drawing.SystemColors.Window;
+ this.mThrobber.Location = new System.Drawing.Point(503, 81);
+ this.mThrobber.Name = "mThrobber";
+ this.mThrobber.Size = new System.Drawing.Size(16, 16);
+ this.mThrobber.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.mThrobber.TabIndex = 4;
+ this.mThrobber.TabStop = false;
+ this.mThrobber.Visible = false;
+ //
+ // mResultsUpdater
+ //
+ this.mResultsUpdater.Interval = 250;
+ this.mResultsUpdater.Tick += new System.EventHandler(this.mResultsUpdater_Tick);
+ //
+ // mNoResultsLabel
+ //
+ this.mNoResultsLabel.AutoSize = true;
+ this.mNoResultsLabel.Location = new System.Drawing.Point(5, 103);
+ this.mNoResultsLabel.Name = "mNoResultsLabel";
+ this.mNoResultsLabel.Size = new System.Drawing.Size(84, 13);
+ this.mNoResultsLabel.TabIndex = 5;
+ this.mNoResultsLabel.Text = "No results found";
+ //
+ // SearchWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.Window;
+ this.ClientSize = new System.Drawing.Size(523, 274);
+ this.ControlBox = false;
+ this.Controls.Add(this.mNoResultsLabel);
+ this.Controls.Add(this.mThrobber);
+ this.Controls.Add(this.mLayout);
+ this.MinimumSize = new System.Drawing.Size(160, 96);
+ this.Name = "SearchWindow";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ this.TopMost = true;
+ this.mLayout.ResumeLayout(false);
+ this.mLayout.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.mBanner)).EndInit();
+ this.mInfoBanner.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.mInfoBannerImage)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.mThrobber)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox mSearch;
+ private System.Windows.Forms.ListBox mResults;
+ private System.Windows.Forms.TableLayoutPanel mLayout;
+ private System.Windows.Forms.PictureBox mBanner;
+ private PictureBox mThrobber;
+ private Timer mResultsUpdater;
+ private Label mNoResultsLabel;
+ private Label mInfoLabel;
+ private Panel mInfoBanner;
+ private PictureBox mInfoBannerImage;
+ }
+}
\ No newline at end of file
diff --git a/AutoTypeSearch/SearchWindow.cs b/AutoTypeSearch/SearchWindow.cs
new file mode 100755
index 0000000..363b898
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.cs
@@ -0,0 +1,925 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Reflection;
+using System.Text;
+using System.Windows.Forms;
+using AutoTypeSearch.Properties;
+using KeePass.Forms;
+using KeePass.Resources;
+using KeePass.UI;
+using KeePass.Util;
+using KeePassLib;
+using KeePassLib.Collections;
+using KeePassLib.Native;
+
+namespace AutoTypeSearch
+{
+ public partial class SearchWindow : Form
+ {
+ private const int SecondLineInset = 10;
+
+ // HACK to work around mono bug
+ private static readonly FieldInfo sMonoListBoxTopIndex = typeof(ListBox).GetField("top_index", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ private readonly MainForm mMainForm;
+ private readonly Bitmap mBannerImage;
+ private readonly Searcher mSearcher;
+
+ private readonly Stream mThrobberImageStream;
+
+ private int? mWindowTopBorderHeight;
+ private int mBannerWidth = -1;
+ private int mMaximumExpandHeight;
+ private bool mManualSizeApplied;
+ private SearchResults mCurrentSearch;
+ private SearchResults mLastResultsUpdated;
+ private int mLastResultsUpdatedNextAvailableIndex;
+
+ #region Opening
+ public SearchWindow()
+ {
+ InitializeComponent();
+
+ // Mono can't load animated gifs from resx without crashing, so load it from an embedded resource instead
+ try
+ {
+ mThrobberImageStream = GetType().Assembly.GetManifestResourceStream("AutoTypeSearch.Throbber.gif");
+ if (mThrobberImageStream != null)
+ {
+ mThrobber.Image = Image.FromStream(mThrobberImageStream);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to load Throbber.gif from embedded resource: " + ex.Message);
+ }
+
+ GlobalWindowManager.CustomizeControl(this);
+ UIUtil.SetExplorerTheme(mResults, true);
+ SetItemHeight();
+ }
+
+ public SearchWindow(MainForm mainForm, string infoBanner) : this()
+ {
+ mMainForm = mainForm;
+
+ mInfoBanner.Height = Math.Max(mInfoBannerImage.Height, mInfoLabel.Font.Height) + mInfoBanner.Margin.Vertical;
+ mInfoLabel.Padding = new Padding(0, (mInfoBanner.Height - mInfoLabel.Font.Height) / 2, 0, 0);
+ mInfoLabel.Text = infoBanner;
+
+ if (infoBanner == null)
+ {
+ mInfoBanner.Visible = false;
+ mInfoBanner.Height = 0;
+ }
+
+ mSearcher = new Searcher(mMainForm.DocumentManager.GetOpenDatabases().ToArray());
+
+ Icon = mMainForm.Icon;
+ using (var bannerIcon = new Icon(Icon, 48, 48))
+ {
+ mBannerImage = bannerIcon.ToBitmap();
+ }
+ UpdateBanner();
+
+ ShowThrobber = false;
+
+ FontUtil.AssignDefaultItalic(mNoResultsLabel);
+ }
+
+
+ protected override void OnCreateControl()
+ {
+ base.OnCreateControl();
+
+ if (NativeMethods.IsWindows10())
+ {
+ mWindowTopBorderHeight = PointToScreen(Point.Empty).Y - this.Top;
+ NativeMethods.RefreshWindowFrame(Handle);
+ }
+
+ var windowRect = Settings.Default.WindowPosition;
+ var collapsedWindowRect = windowRect;
+
+ collapsedWindowRect.Height = mSearch.Bottom + (Height - ClientSize.Height);
+
+ MinimumSize = new Size(MinimumSize.Width, collapsedWindowRect.Height);
+
+ if (windowRect.IsEmpty || !IsOnScreen(collapsedWindowRect))
+ {
+ windowRect = new Rectangle(0, 0, Width, Height);
+ Height = collapsedWindowRect.Height;
+
+ CenterToScreen();
+ }
+ else
+ {
+ Location = windowRect.Location;
+ Size = collapsedWindowRect.Size;
+ }
+
+ mMaximumExpandHeight = Math.Max(windowRect.Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+
+
+ private static bool IsOnScreen(Rectangle rectangle)
+ {
+ return Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(rectangle));
+ }
+
+ private void SetItemHeight()
+ {
+ mResults.ItemHeight = mResults.Font.Height * 2 + 2;
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (mWindowTopBorderHeight.HasValue)
+ {
+ NativeMethods.RemoveWindowFrameTopBorder(ref m, mWindowTopBorderHeight.Value);
+ }
+ base.WndProc(ref m);
+ }
+
+ #endregion
+
+ #region Closing
+ protected override void OnActivated(EventArgs e)
+ {
+ base.OnActivated(e);
+ Deactivate += OnDeactivate;
+ }
+
+ private void OnDeactivate(object sender, EventArgs eventArgs)
+ {
+ Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ Deactivate -= OnDeactivate;
+ base.OnClosed(e);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ if (mBannerImage != null)
+ {
+ mBannerImage.Dispose();
+ }
+ if (mThrobber.Image != null)
+ {
+ mThrobber.Image.Dispose();
+ mThrobber.Image = null;
+ mThrobberImageStream.Dispose();
+ }
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ #endregion
+
+ #region Item Drawing
+ private void mResults_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ var searchResult = mResults.Items[e.Index] as SearchResult;
+ if (searchResult == null)
+ {
+ Debug.Fail("Unexpected item in mResults");
+// ReSharper disable once HeuristicUnreachableCode - Not unreachable
+ return;
+ }
+ var drawingArea = e.Bounds;
+ drawingArea.Height--; // Leave room for a dividing line at the bottom
+
+ if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
+ {
+ DrawBorderedRectangle(e.Graphics, drawingArea, SystemColors.Highlight);
+ }
+ else
+ {
+ e.Graphics.FillRectangle(SystemBrushes.Window, drawingArea);
+ }
+
+ var image = GetImage(searchResult.Database, searchResult.Entry.CustomIconUuid, searchResult.Entry.IconId);
+ var imageMargin = (drawingArea.Height - image.Height) / 2;
+ e.Graphics.DrawImage(image, drawingArea.Left + imageMargin, drawingArea.Top + imageMargin, image.Width, image.Height);
+
+ var textLeftMargin = drawingArea.Left + imageMargin * 2 + image.Width;
+ var textBounds = new Rectangle(textLeftMargin, drawingArea.Top + 1, drawingArea.Width - textLeftMargin - 1, drawingArea.Height - 2);
+
+ var line1Bounds = textBounds;
+ line1Bounds.Height = e.Font.Height;
+ var line2Bounds = line1Bounds;
+ line2Bounds.Y += line2Bounds.Height - 1;
+ line2Bounds.X += SecondLineInset;
+ line2Bounds.Width -= SecondLineInset;
+
+ var resultInTitleField = searchResult.FieldName == PwDefs.TitleField;
+
+ var title = (resultInTitleField ? searchResult.FieldValue : searchResult.Title).Replace('\n', ' '); // The FieldValue may have references resolved, whereas the title is always read directly.
+
+ var uniqueTitlePartWidth = 0;
+ if (!String.IsNullOrEmpty(searchResult.UniqueTitlePart))
+ {
+ var uniqueTitlePart = searchResult.UniqueTitlePart.Replace('\n', ' ');
+
+ var titleWidth = TextRenderer.MeasureText(e.Graphics, title, e.Font, line1Bounds.Size, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+
+ var availableWidthForUniqueTitlePart = line1Bounds.Width - titleWidth;
+ if (availableWidthForUniqueTitlePart > 20) // Don't bother including a unique part if there's no room for it
+ {
+ var uniqueTitlePartReversed = ReverseString(uniqueTitlePart);
+
+ uniqueTitlePartWidth = TextRenderer.MeasureText(e.Graphics, uniqueTitlePartReversed, e.Font, new Size(availableWidthForUniqueTitlePart, line1Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ uniqueTitlePart = ReverseString(uniqueTitlePartReversed);
+
+ TextRenderer.DrawText(e.Graphics, uniqueTitlePart, e.Font, new Rectangle(line1Bounds.X, line1Bounds.Y, uniqueTitlePartWidth, line1Bounds.Height), SystemColors.GrayText, TextFormatFlags.NoPadding);
+ }
+ }
+
+ var titleBounds = new Rectangle(line1Bounds.X + uniqueTitlePartWidth, line1Bounds.Y, line1Bounds.Width - uniqueTitlePartWidth, line1Bounds.Height);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Highlight title in first line.
+ DrawHighlight(e, titleBounds, title, searchResult.Start, searchResult.Length);
+ }
+
+ TextRenderer.DrawText(e.Graphics, searchResult.Title, e.Font, titleBounds, SystemColors.WindowText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+
+ if (resultInTitleField)
+ {
+ // Found the result in the title field. Use Username for second line.
+ TextRenderer.DrawText(e.Graphics, KPRes.UserName + ": " + searchResult.Entry.Strings.ReadSafeEx(PwDefs.UserNameField), e.Font, line2Bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ else
+ {
+ // Found the result in not title field. Show the matching result on second line
+
+ var fieldValue = searchResult.FieldValue.Replace('\n',' ');
+ var fieldNamePrefix = GetDisplayFieldName(searchResult.FieldName) + ": ";
+
+ var remainingSpace = line2Bounds.Width;
+ var fieldNamePrefixWidth = TextRenderer.MeasureText(e.Graphics, fieldNamePrefix, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldNamePrefixWidth;
+
+ int fieldValueHighlightWidth = 0, fieldValueLeftContextWidth = 0, fieldValueRightContextWidth = 0;
+
+ var leftContext = fieldValue.Substring(0, searchResult.Start);
+ var highlight = fieldValue.Substring(searchResult.Start, searchResult.Length);
+ var rightContext = fieldValue.Substring(searchResult.Start + searchResult.Length);
+
+ if (searchResult.Length == 0)
+ {
+ fieldValueHighlightWidth = remainingSpace;
+ }
+ else
+ {
+ if (remainingSpace > 0)
+ {
+ var availableSpace = remainingSpace;
+ fieldValueHighlightWidth = TextRenderer.MeasureText(e.Graphics, highlight, e.Font, new Size(availableSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ remainingSpace -= fieldValueHighlightWidth;
+ }
+
+ // Of the space remaining, divide it equally between that which comes before, and that which comes after
+ if (!String.IsNullOrEmpty(leftContext))
+ {
+ var leftContextReversed = ReverseString(leftContext);
+ fieldValueLeftContextWidth = TextRenderer.MeasureText(e.Graphics, leftContextReversed, e.Font, new Size(remainingSpace / 2, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis | TextFormatFlags.ModifyString).Width;
+
+ if (fieldValueLeftContextWidth > remainingSpace)
+ {
+ // Always allow space for the minimal left context
+ fieldValueHighlightWidth -= (fieldValueLeftContextWidth - remainingSpace);
+ remainingSpace = 0;
+ }
+ else
+ {
+ remainingSpace -= fieldValueLeftContextWidth;
+ }
+
+ // Replace left context with the truncated reversed left context.
+ leftContext = ReverseString(leftContextReversed);
+ }
+
+ if (remainingSpace > 0 && !String.IsNullOrEmpty(rightContext))
+ {
+ fieldValueRightContextWidth = TextRenderer.MeasureText(e.Graphics, rightContext, e.Font, new Size(remainingSpace, line2Bounds.Height), TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis).Width;
+ if (fieldValueRightContextWidth > remainingSpace)
+ {
+ fieldValueRightContextWidth = 0;
+ }
+ }
+ }
+
+ // Now draw it all
+ var bounds = line2Bounds;
+ bounds.Width = fieldNamePrefixWidth;
+ TextRenderer.DrawText(e.Graphics, fieldNamePrefix, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ if (fieldValueLeftContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueLeftContextWidth;
+ TextRenderer.DrawText(e.Graphics, leftContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding); // No ellipsis as the leftContext string has already been truncated appropriately
+ }
+ if (fieldValueHighlightWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueHighlightWidth;
+
+ if (searchResult.Length > 0)
+ {
+ DrawHighlightRectangle(e, bounds);
+ }
+ TextRenderer.DrawText(e.Graphics, highlight, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ if (fieldValueRightContextWidth > 0)
+ {
+ bounds.X += bounds.Width;
+ bounds.Width = fieldValueRightContextWidth;
+ TextRenderer.DrawText(e.Graphics, rightContext, e.Font, bounds, SystemColors.GrayText, TextFormatFlags.NoPadding | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ e.Graphics.DrawLine(SystemPens.ButtonFace, drawingArea.Left, drawingArea.Bottom, drawingArea.Right, drawingArea.Bottom);
+ }
+
+ private static string ReverseString(string value)
+ {
+ return new String(value.ToCharArray().TakeWhile(c => c != '\0').Reverse().ToArray());
+ }
+
+ private static void DrawHighlight(DrawItemEventArgs e, Rectangle lineBounds, string text, int highlightFrom, int highlightLength)
+ {
+ var highlightX = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width;
+ var highlightWidth = TextRenderer.MeasureText(e.Graphics, text.Substring(0, highlightFrom + highlightLength), e.Font, Size.Empty, TextFormatFlags.NoPadding).Width - highlightX;
+
+ DrawHighlightRectangle(e, new Rectangle(lineBounds.Left + highlightX, lineBounds.Top, highlightWidth, lineBounds.Height));
+ }
+
+ private static void DrawHighlightRectangle(DrawItemEventArgs e, Rectangle rectangle)
+ {
+ DrawBorderedRectangle(e.Graphics, rectangle, Color.PaleTurquoise);
+ }
+
+ private static void DrawBorderedRectangle(Graphics graphics, Rectangle rectangle, Color colour)
+ {
+ var border = rectangle;
+ border.Width--;
+ border.Height--;
+
+ using (var brush = new SolidBrush(MergeColors(colour, SystemColors.Window, 0.2)))
+ {
+ graphics.FillRectangle(brush, rectangle);
+ }
+ using (var pen = new Pen(colour, 1f))
+ {
+ graphics.DrawRectangle(pen, border);
+ }
+ }
+
+ private Image GetImage(PwDatabase database, PwUuid customIconId, PwIcon iconId)
+ {
+ Image image = null;
+ if (!customIconId.Equals(PwUuid.Zero))
+ {
+ image = database.GetCustomIcon(customIconId, DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
+ }
+ if (image == null)
+ {
+ try { image = mMainForm.ClientIcons.Images[(int)iconId]; }
+ catch (Exception) { Debug.Assert(false); }
+ }
+
+ return image;
+ }
+
+ private static string GetDisplayFieldName(string fieldName)
+ {
+ switch (fieldName)
+ {
+ case PwDefs.TitleField:
+ return KPRes.Title;
+ case PwDefs.UserNameField:
+ return KPRes.UserName;
+ case PwDefs.PasswordField:
+ return KPRes.Password;
+ case PwDefs.UrlField:
+ return KPRes.Url;
+ case PwDefs.NotesField:
+ return KPRes.Notes;
+ case AutoTypeSearchExt.TagsVirtualFieldName:
+ return KPRes.Tags;
+ default:
+ return fieldName;
+ }
+ }
+
+ public static Color MergeColors(Color from, Color to, double amount)
+ {
+ var r = (byte)((from.R * amount) + to.R * (1 - amount));
+ var g = (byte)((from.G * amount) + to.G * (1 - amount));
+ var b = (byte)((from.B * amount) + to.B * (1 - amount));
+ return Color.FromArgb(r, g, b);
+ }
+ #endregion
+
+ #region Mouse tracking
+ private Point mMouseEntryPosition;
+
+ private void mResults_MouseEnter(object sender, EventArgs e)
+ {
+ mMouseEntryPosition = MousePosition;
+ }
+
+ private void mResults_MouseMove(object sender, MouseEventArgs e)
+ {
+ // Discard the location the mouse has on entering the control (as it may be that the control has just moved under the mouse, not the other way around)
+ if (MousePosition == mMouseEntryPosition)
+ {
+ return;
+ }
+
+ // Hot tracking
+ var hoverIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (hoverIndex >= 0 && mResults.SelectedIndex != hoverIndex)
+ {
+ if (mResults.GetItemRectangle(hoverIndex).Bottom <= mResults.ClientRectangle.Bottom)
+ {
+ mResults.SelectedIndex = hoverIndex;
+ }
+ else
+ {
+ // Avoid the control scrolling
+ mResults.BeginUpdate();
+ var topIndex = mResults.TopIndex;
+ mResults.SelectedIndex = hoverIndex;
+ mResults.TopIndex = topIndex;
+ mResults.EndUpdate();
+ }
+ }
+ }
+ #endregion
+
+ #region Resizing
+ protected override void OnResizeBegin(EventArgs e)
+ {
+ // Stop automatically sizing - the user is picking a size they want.
+ mManualSizeApplied = true;
+ base.OnResizeBegin(e);
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+
+ UpdateBanner();
+
+ mResults.Invalidate();
+ }
+
+ protected override void OnResizeEnd(EventArgs e)
+ {
+ base.OnResizeEnd(e);
+
+ if (Height > MinimumSize.Height && Height != mMaximumExpandHeight)
+ {
+ mMaximumExpandHeight = Math.Max(Height, MinimumSize.Height + mResults.ItemHeight);
+ }
+ else
+ {
+ mManualSizeApplied = false;
+ }
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+
+ private void UpdateBanner()
+ {
+ if (mBannerImage != null)
+ {
+ BannerFactory.UpdateBanner(this, mBanner, mBannerImage, PwDefs.ProductName, Resources.BannerText, ref mBannerWidth);
+ }
+ }
+
+ private void mSearch_LocationChanged(object sender, EventArgs e)
+ {
+ mThrobber.Location = new Point(mSearch.Right - mThrobber.Width - mThrobber.Margin.Right, mSearch.Top + (mSearch.Height - mThrobber.Height) / 2);
+ }
+
+ private void mResults_LocationChanged(object sender, EventArgs e)
+ {
+ mNoResultsLabel.Top = mResults.Top + (mResults.ItemHeight - mNoResultsLabel.Height) / 2;
+ }
+ #endregion
+
+ #region Searching
+ private static readonly SearchResultPrecedence SearchResultPrecedenceComparer = new SearchResultPrecedence();
+ private void mSearch_TextChanged(object sender, EventArgs e)
+ {
+ if (mSearch.Text.Length < 2)
+ {
+ // Stop searching
+ mResultsUpdater.Enabled = false;
+ ShowThrobber = false;
+ Height = MinimumSize.Height;
+ mManualSizeApplied = false;
+ mResults.Items.Clear();
+ mLastResultsUpdated = null;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ else
+ {
+ // Start searching
+ mNoResultsLabel.Visible = false;
+ mCurrentSearch = mSearcher.Search(mSearch.Text);
+ mResultsUpdater.Enabled = true;
+ ShowThrobber = true;
+ mResultsUpdater_Tick(null, EventArgs.Empty); // Quick poke just in case the results are already done.
+ }
+ }
+
+ [SuppressMessage("ReSharper", "CoVariantArrayConversion", Justification = "Object arrays for Listbox.Items, known to be of correct type")]
+ private void mResultsUpdater_Tick(object sender, EventArgs e)
+ {
+ if (mLastResultsUpdated != mCurrentSearch)
+ {
+ // Clear out old results and replace with new ones
+ mResults.Items.Clear();
+ mLastResultsUpdated = mCurrentSearch;
+ mLastResultsUpdatedNextAvailableIndex = 0;
+ }
+ var existingResultsCount = mResults.Items.Count;
+
+ bool complete;
+ var newResults = mLastResultsUpdated.GetAvailableResults(ref mLastResultsUpdatedNextAvailableIndex, out complete);
+ if (newResults.Length > 0)
+ {
+ mResults.BeginUpdate();
+
+ SearchResult[] allResults;
+ if (existingResultsCount > 0)
+ {
+ allResults = new SearchResult[existingResultsCount + newResults.Length];
+ mResults.Items.CopyTo(allResults, 0);
+ newResults.CopyTo(allResults, existingResultsCount);
+
+ mResults.Items.Clear();
+ }
+ else
+ {
+ allResults = newResults;
+ }
+
+ CalculateUniqueTitles(allResults);
+
+ Array.Sort(allResults, SearchResultPrecedenceComparer);
+ mResults.Items.AddRange(allResults);
+
+ mResults.EndUpdate();
+
+ if (allResults.Length > 0)
+ {
+ if (mResults.SelectedIndex == -1)
+ {
+ try
+ {
+ // HACK to work around mono bug
+ if (sMonoListBoxTopIndex != null)
+ {
+ sMonoListBoxTopIndex.SetValue(mResults, 1); // Set the top_index to 1 so that when selected index is set to 0, and calls EnsureVisible(0), it follows the index < top_index pass and not the broken index >= top_index + rows path.
+ }
+
+ mResults.SelectedIndex = 0;
+ mResults.TopIndex = 0;
+ }
+ catch (Exception ex)
+ {
+ Debug.Fail("Failed to set selection on count of " + allResults.Length + ": " + ex.Message);
+ }
+ }
+
+ if (!mManualSizeApplied)
+ {
+ Height = Math.Min(mMaximumExpandHeight, MinimumSize.Height + (allResults.Length * mResults.ItemHeight));
+ }
+ }
+ }
+
+ if (complete)
+ {
+ ShowThrobber = false;
+ mResultsUpdater.Enabled = false;
+
+ if (mResults.Items.Count == 0)
+ {
+ mNoResultsLabel.Visible = true;
+ Height = MinimumSize.Height + mResults.ItemHeight;
+ mManualSizeApplied = false;
+ }
+ }
+ }
+
+ private void CalculateUniqueTitles(IEnumerable results, int depth = 0)
+ {
+ // Where results have identical titles, include group titles to make them unique
+ depth += 1;
+
+ // First create a lookup by title
+ var titles = new Dictionary>();
+ foreach (var searchResult in results)
+ {
+ List resultsWithSameTitle;
+ if (titles.TryGetValue(searchResult.UniqueTitle, out resultsWithSameTitle))
+ {
+ resultsWithSameTitle.Add(searchResult);
+ }
+ else
+ {
+ titles.Add(searchResult.UniqueTitle, new List { searchResult });
+ }
+ }
+
+ // Attempt to unique-ify any non-unique titles
+ foreach (var resultsSharingTitle in titles.Values)
+ {
+ if (resultsSharingTitle.Count > 1)
+ {
+ var titlesModified = false;
+ foreach (var searchResult in resultsSharingTitle)
+ {
+ titlesModified |= searchResult.SetUniqueTitleDepth(depth);
+ }
+
+ if (titlesModified)
+ {
+ // Recurse in case of continuing non-uniqueness
+ CalculateUniqueTitles(resultsSharingTitle, depth);
+ }
+ }
+ }
+ }
+
+ private class SearchResultPrecedence : IComparer
+ {
+ public int Compare(SearchResult x, SearchResult y)
+ {
+ // First precedence is that if the result is the start of the field value, it's higher precedence than if it doesn't.
+ var result = -(x.Start == 0).CompareTo(y.Start == 0);
+
+ // Second precedence is that the start of the title field is higher precedence than the start of any other field
+ if (result == 0)
+ {
+ result = -(x.FieldName == PwDefs.TitleField).CompareTo(y.FieldName == PwDefs.TitleField);
+ }
+
+ // Both start the title field, so both equal. Have to have consistent ordering, so return final precedence based search index
+ if (result == 0)
+ {
+ result = x.ResultIndex.CompareTo(y.ResultIndex);
+ }
+
+ return result;
+ }
+ }
+
+ private bool ShowThrobber
+ {
+ get { return mThrobber.Visible; }
+ set
+ {
+ if (value != ShowThrobber)
+ {
+ if (value)
+ {
+ mThrobber.Visible = true;
+
+ // Set the margin on the textbox to allow room for the throbber
+ NativeMethods.SetTextBoxRightMargin(mSearch, mThrobber.Width + mThrobber.Margin.Right);
+ }
+ else
+ {
+ mThrobber.Visible = false;
+
+ NativeMethods.SetTextBoxRightMargin(mSearch, 0);
+ }
+ }
+ }
+ }
+ #endregion
+
+ private void mBannerImage_MouseDown(object sender, MouseEventArgs e)
+ {
+ // Allow drag by banner image
+ if (e.Button == MouseButtons.Left)
+ {
+ if (e.Clicks == 2)
+ {
+ // Re-center the form on double-click
+ CenterToScreen();
+
+ Settings.Default.WindowPosition = new Rectangle(Left, Top, Width, mMaximumExpandHeight);
+ }
+ else if (!NativeLib.IsUnix())
+ {
+ NativeMethods.StartFormDrag(this);
+ }
+ }
+ }
+
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ switch (keyData)
+ {
+ case Keys.Escape:
+ Close();
+ return true;
+ case Keys.Up:
+ TryChangeSelection(-1);
+ return true;
+ case Keys.Down:
+ TryChangeSelection(1);
+ return true;
+ case Keys.PageUp:
+ TryChangeSelection(-mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.PageDown:
+ TryChangeSelection(mResults.ClientSize.Height / mResults.ItemHeight);
+ return true;
+ case Keys.Home | Keys.Control:
+ mResults.SelectedIndex = 0;
+ return true;
+ case Keys.End | Keys.Control:
+ mResults.SelectedIndex = mResults.Items.Count - 1;
+ return true;
+ case Keys.Enter:
+ PerformAction(Settings.Default.DefaultAction, mResults.SelectedItem as SearchResult);
+ break;
+ case Keys.Enter | Keys.Shift:
+ PerformAction(Settings.Default.AlternativeAction, mResults.SelectedItem as SearchResult);
+ break;
+ }
+
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #region Selection Changing
+
+ protected override void OnMouseWheel(MouseEventArgs e)
+ {
+ mResults.TopIndex -= (e.Delta / Math.Abs(e.Delta));
+ }
+
+ private void TryChangeSelection(int delta)
+ {
+ if (mResults.Items.Count > 0)
+ {
+ mResults.SelectedIndex = Math.Max(Math.Min(mResults.Items.Count - 1, mResults.SelectedIndex + delta), 0);
+ }
+ }
+ #endregion
+
+ #region Actions
+
+ private void mResults_MouseClick(object sender, MouseEventArgs e)
+ {
+ var clickIndex = mResults.IndexFromPoint(e.X, e.Y);
+ if (clickIndex >= 0)
+ {
+ var clickedResult = mResults.Items[clickIndex] as SearchResult;
+ if (clickedResult != null)
+ {
+ PerformAction((ModifierKeys & Keys.Shift) == Keys.Shift ? Settings.Default.AlternativeAction : Settings.Default.DefaultAction, clickedResult);
+ }
+ }
+ }
+
+ private void PerformAction(Actions action, SearchResult searchResult)
+ {
+ Close();
+
+ if (searchResult != null)
+ {
+ switch (action)
+ {
+ case Actions.PerformAutoType:
+ AutoTypeEntry(searchResult);
+ break;
+ case Actions.EditEntry:
+ EditEntry(searchResult);
+ break;
+ case Actions.ShowEntry:
+ ShowEntry(searchResult);
+ break;
+ case Actions.OpenEntryUrl:
+ OpenEntryUrl(searchResult);
+ break;
+ case Actions.CopyPassword:
+ CopyPassword(searchResult);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("action");
+ }
+ }
+ }
+
+ private void AutoTypeEntry(SearchResult searchResult)
+ {
+ bool result;
+ if (ActiveForm != null)
+ {
+ result = AutoType.PerformIntoPreviousWindow(mMainForm, searchResult.Entry, searchResult.Database);
+ }
+ else
+ {
+ result = AutoType.PerformIntoCurrentWindow(searchResult.Entry, searchResult.Database);
+ }
+ if (!result)
+ {
+ SystemSounds.Beep.Play();
+
+ if (Settings.Default.AlternativeAction != Actions.PerformAutoType)
+ {
+ PerformAction(Settings.Default.AlternativeAction, searchResult);
+ }
+ }
+ }
+
+ private void EditEntry(SearchResult searchResult)
+ {
+ using (var entryForm = new PwEntryForm())
+ {
+ mMainForm.MakeDocumentActive(mMainForm.DocumentManager.FindDocument(searchResult.Database));
+
+ entryForm.InitEx(searchResult.Entry, PwEditMode.EditExistingEntry, searchResult.Database, mMainForm.ClientIcons, false, false);
+
+ ShowForegroundDialog(entryForm);
+
+ mMainForm.UpdateUI(false, null, searchResult.Database.UINeedsIconUpdate, null, true, null, entryForm.HasModifiedEntry);
+ }
+ }
+
+// ReSharper disable once UnusedMethodReturnValue.Local - Generic helper, result may be used in future
+ private DialogResult ShowForegroundDialog(Form form)
+ {
+ mMainForm.EnsureVisibleForegroundWindow(false, false);
+ form.StartPosition = FormStartPosition.CenterScreen;
+ if (mMainForm.IsTrayed())
+ {
+ form.ShowInTaskbar = true;
+ }
+
+ form.Shown += ActivateFormOnShown;
+ return form.ShowDialog(mMainForm);
+ }
+
+ private static void ActivateFormOnShown(object sender, EventArgs eventArgs)
+ {
+ var form = (Form)sender;
+ form.Shown -= ActivateFormOnShown;
+ form.Activate();
+ }
+
+ private void ShowEntry(SearchResult searchResult)
+ {
+ // Show this entry
+ mMainForm.UpdateUI(false, mMainForm.DocumentManager.FindDocument(searchResult.Database), true, searchResult.Entry.ParentGroup, true, null, false, null);
+ mMainForm.SelectEntries(new PwObjectList { searchResult.Entry }, true, true);
+ mMainForm.EnsureVisibleEntry(searchResult.Entry.Uuid);
+ mMainForm.UpdateUI(false, null, false, null, false, null, false);
+ mMainForm.EnsureVisibleForegroundWindow(true, true);
+ }
+
+ private void OpenEntryUrl(SearchResult searchResult)
+ {
+ WinUtil.OpenEntryUrl(searchResult.Entry);
+ }
+
+ private void CopyPassword(SearchResult searchResult)
+ {
+ if (ClipboardUtil.Copy(searchResult.Entry.Strings.ReadSafe(PwDefs.PasswordField), true, true, searchResult.Entry,
+ mMainForm.DocumentManager.SafeFindContainerOf(searchResult.Entry),
+ IntPtr.Zero))
+ {
+ mMainForm.StartClipboardCountdown();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/AutoTypeSearch/SearchWindow.resx b/AutoTypeSearch/SearchWindow.resx
new file mode 100755
index 0000000..8ef82f0
--- /dev/null
+++ b/AutoTypeSearch/SearchWindow.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/AutoTypeSearch/Searcher.cs b/AutoTypeSearch/Searcher.cs
new file mode 100755
index 0000000..433ae94
--- /dev/null
+++ b/AutoTypeSearch/Searcher.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using AutoTypeSearch.Properties;
+using KeePassLib;
+
+namespace AutoTypeSearch
+{
+ internal class Searcher
+ {
+ private readonly PwDatabase[] mDatabases;
+ private readonly Dictionary mSearches = new Dictionary();
+
+ public Searcher(PwDatabase[] databases)
+ {
+ mDatabases = databases;
+ }
+
+ public SearchResults Search(string term)
+ {
+ if (term.Length < 2)
+ {
+ throw new ArgumentException("Search term must be at least 2 characters");
+ }
+
+ SearchResults parentResults = null;
+
+ var termParent = term;
+ while (termParent.Length >= 2)
+ {
+ if (mSearches.TryGetValue(termParent, out parentResults))
+ {
+ if (termParent == term)
+ {
+ // This is an exact duplicate search, so return it.
+ return parentResults;
+ }
+
+ // Found an existing search for a parent of the term, start from there.
+ break;
+ }
+
+ // No existing search for termParent found, try less.
+ termParent = termParent.Remove(termParent.Length - 1, 1);
+ }
+
+ SearchResults searchResults;
+ if (parentResults == null)
+ {
+ // No parent found at all, start from scratch
+ searchResults = new SearchResults(GetCountOfAllDatabaseEntries(), term);
+
+ var rootSearchThread = new Thread(RootSearchWorker) { Name = term };
+ rootSearchThread.Start(searchResults);
+ }
+ else
+ {
+ searchResults = parentResults.CreateChildResults(term);
+
+ var childSearchThread = new Thread(ChildSearchWorker) { Name = term };
+ childSearchThread.Start(new ChildSearchWorkerState{ Source = parentResults, Results = searchResults });
+ }
+
+ mSearches.Add(term, searchResults);
+
+ return searchResults;
+ }
+
+ private int GetCountOfAllDatabaseEntries()
+ {
+ return (from database in mDatabases select (int)database.RootGroup.GetEntriesCount(true)).Sum();
+ }
+
+ private void RootSearchWorker(object stateObject)
+ {
+ var results = (SearchResults)stateObject;
+ var excludeExpired = Settings.Default.ExcludeExpired;
+ var searchStartTime = DateTime.Now;
+
+ foreach (var database in mDatabases)
+ {
+ SearchGroup(database, database.RootGroup, results, excludeExpired, searchStartTime);
+ }
+
+ results.SetComplete();
+ }
+
+ ///
+ /// Recursively search and its children, adding results to
+ ///
+ private void SearchGroup(PwDatabase context, PwGroup group, SearchResults results, bool excludeExpired, DateTime searchStartTime)
+ {
+ if (group.EnableSearching ?? true) // Group will only be searched if it's parent enabled searching, so if it is inherit (null) or true, search it.
+ {
+ foreach (var childGroup in group.Groups)
+ {
+ SearchGroup(context, childGroup, results, excludeExpired, searchStartTime);
+ }
+
+ foreach (var entry in group.Entries)
+ {
+ if (!(excludeExpired && entry.Expires && searchStartTime > entry.ExpiryTime))
+ {
+ results.AddResultIfMatchesTerm(context, entry);
+ }
+ }
+ }
+ }
+
+ private struct ChildSearchWorkerState
+ {
+ public SearchResults Source;
+ public SearchResults Results;
+ }
+ private void ChildSearchWorker(object stateObject)
+ {
+ var state = (ChildSearchWorkerState)stateObject;
+
+ bool complete;
+ var index = 0;
+ do
+ {
+ foreach (var entry in state.Source.GetAvailableResults(ref index, out complete))
+ {
+ state.Results.AddResultIfMatchesTerm(entry);
+ }
+ } while (!complete);
+
+ state.Results.SetComplete();
+ }
+ }
+}
diff --git a/AutoTypeSearch/Throbber.gif b/AutoTypeSearch/Throbber.gif
new file mode 100755
index 0000000..494d426
--- /dev/null
+++ b/AutoTypeSearch/Throbber.gif
Binary files differ
diff --git a/AutoTypeSearch/app.config b/AutoTypeSearch/app.config
new file mode 100755
index 0000000..1370758
--- /dev/null
+++ b/AutoTypeSearch/app.config
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ 0, 0, 0, 0
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ PerformAutoType
+
+
+ EditEntry
+
+
+ None
+
+
+
+
\ No newline at end of file
diff --git a/CreatePlgX.bat b/CreatePlgX.bat
new file mode 100755
index 0000000..59b9aa0
--- /dev/null
+++ b/CreatePlgX.bat
@@ -0,0 +1,20 @@
+@echo off
+cd %~dp0
+
+echo Deleting existing PlgX folder
+rmdir /s /q PlgX
+
+echo Creating PlgX folder
+mkdir PlgX
+
+echo Copying files
+xcopy "AutoTypeSearch" PlgX /s /e /exclude:PlgXExclude.txt
+
+echo Compiling PlgX
+"../KeePass/KeePass.exe" /plgx-create "%~dp0PlgX" --plgx-prereq-kp:2.27
+
+echo Releasing PlgX
+move /y PlgX.plgx "Releases\Build Outputs\AutoTypeSearch.plgx"
+
+echo Cleaning up
+rmdir /s /q PlgX
diff --git a/PlgXExclude.txt b/PlgXExclude.txt
new file mode 100755
index 0000000..f626d58
--- /dev/null
+++ b/PlgXExclude.txt
@@ -0,0 +1,7 @@
+\bin\
+\obj\
+.user
+.sln
+.suo
+.pdb
+.xml
\ No newline at end of file
diff --git a/Readme.txt b/Readme.txt
new file mode 100755
index 0000000..96d2fc0
--- /dev/null
+++ b/Readme.txt
@@ -0,0 +1,103 @@
+AutoTypeSearch
+==============
+http://sourceforge.net/projects/autotypesearch
+
+
+This is a plugin to KeePass to provide a quick searching capability as
+an enhancement to the global auto-type system. If a global auto-type is requested, but no matching
+entry for the active window is found, this plugin will show a quick as-you-type search window which
+lets you to easily pick the entry to auto-type.
+
+
+Installation
+------------
+Place AutoTypeSearch.plgx in your KeePass Plugins folder.
+
+
+Usage
+-----
+AutoTypeSearch is initially configured to automatically appear after an unsuccessful global
+auto-type. However, this can be changed in the KeePass Options window (an AutoTypeShow tab has
+been added). Here, a system-wide hot key can be configured to show the AutoTypeSearch window
+immediately. It is also possible to show the window by running KeePass.exe passing "/e1:AutoTypeSearch"
+as a command line parameter.
+
+Once the window is shown, usage is extremely simple. Just start typing, and AutoTypeSearch will
+search your database for matching entries. By default, the Title, Url, Notes, Tags, and Custom Fields
+will be searched, but this can be configured in the AutoTypeShow tab of the KeePass Options window.
+
+Protected fields (like Password) will not be searched.
+
+The arrow keys can be used to move the selection in the list of results, then press Enter to auto-
+type the selected entry. Alternatively, press Shift+Enter to open the entry instead of auto-typing it.
+(These actions can also be customised in the Options window.) Clicking and Shift-Clicking an entry will
+also perform those actions.
+
+
+Uninstallation
+--------------
+Delete AutoTypeSearch.plgx from your KeePass Plugins folder.
+
+
+Checking for updates
+--------------------
+If you want to use the KeePass Check for Updates function to check for updates to this plugin
+then it requires the SourceForgeUpdateChecker plugin to be installed too:
+http://sourceforge.net/projects/kpsfupdatechecker
+
+
+Bug Reporting, Questions, Comments, Feedback, Donations
+-------------------------------------------------------
+Please use the SourceForge project page:
+Bugs can be reported using the issue tracker, for anything else, a discussion forum is available.
+
+
+Changelog
+---------
+v0.1
+ Initial release
+
+v0.2
+ Added information banner when search is shown as a result of an unsuccessful global auto-type
+ Compatibility with Linux/Mono
+
+v0.3
+ Added search result prioritisation for entries where the match is found at the start of the field
+
+v0.4
+ Added support for multiple databases. All currently open, unlocked, databases will be searched
+
+v0.5
+ Added support for KeePass 2.29 high resolution custom icons
+
+v0.6
+ Where title does not uniquely identify the results shown, now also shows the group name as context
+
+v0.7
+ Added support for the "Open entry URL" action. Use the Options window to choose this, if required.
+
+v0.8
+ Added support for the "Copy password" action. Use the Options window to choose this, if required.
+
+v0.9
+ Added workaround for mono bug under Linux that could cause an ArgumentOutOfRange crash when
+ searching if only a single result is initially returned.
+
+v0.91
+ Fixed bug where up or down keys would cause an exception if there are no results to scroll through
+
+v0.10
+ Compatibility with KeePass 2.41 (No longer compatible with previous versions)
+
+v0.11
+ Diacritic (accent) insensitive searching
+
+v0.12
+ Removed ugly white top border under Windows 10
+
+v1.0
+ Compatibility with KeePass 2.42. For versions of KeePass prior to 2.42, use an 0.X version.
+
+Attributions
+------------
+Throbber image by FlipDarius http://www.mediawiki.org/wiki/File:Loading.gif
\ No newline at end of file
diff --git a/Releases/PackageRelease.bat b/Releases/PackageRelease.bat
new file mode 100755
index 0000000..3fb646d
--- /dev/null
+++ b/Releases/PackageRelease.bat
@@ -0,0 +1,23 @@
+@echo off
+set version=1.0
+set output=%~dp0v%version%\
+set zipfile="%output%AutoTypeSearch-v%version%.zip"
+set buildoutputs="%~dp0Build Outputs"
+
+rd /s /q "%output%"
+
+copy "%~dp0..\Readme.txt" %buildoutputs%
+rem copy "%~dp0..\COPYING" %buildoutputs%
+
+rem don't include pdb files
+rem del %buildoutputs%\*.pdb
+
+pushd %buildoutputs%
+"%ProgramFiles%\7-Zip\7z.exe" a -tzip -mx9 -bd %zipfile% *
+popd
+copy "%~dp0..\Readme.txt" "%output%"
+
+set version=
+set output=
+set zipfile=
+set buildoutputs=
\ No newline at end of file