Compare commits

...

55 Commits

Author SHA1 Message Date
Enrico Giordani
ac6a013bf5 FZ Readme and backlog update (#2418) 2020-04-29 10:14:35 -07:00
Clint Rutkas
ae9e257e56 Update NOTICE.md 2020-04-28 10:42:16 -07:00
yuyoyuppe
bb82a9bcf4 FZ: remove RestoreOrginalTransparency() from ZoneWindow::~ZoneWindow() and from ZoneWindow::MoveSizeEnter() 2020-04-28 15:17:20 +03:00
yuyoyuppe
df61ea0a62 Revert "Added a mutex to ZoneWindow, ensured no data races occur (#2154)"
This reverts commit af4678ff18.
2020-04-28 15:17:20 +03:00
Enrico Giordani
feb950be3c Increment version to 0.17.0 (#2415) 2020-04-27 16:25:25 +02:00
Andrey Nekrasov
5b02bda277 runner: download_updates_automatically option is disabled by default for non-admin users (#2412) 2020-04-27 16:02:28 +03:00
Andrey Nekrasov
ce4643d650 runner: autoupdate improvements (#2346) 2020-04-27 13:39:47 +03:00
Yevhenii Holovachov
55c311cd39 "Download updates automatically" toggle enabled only for administrator account (#2357)
* Added condition to disable auto updates toggle in settings for non-administrator accounts
2020-04-27 11:04:20 +03:00
Andrey Nekrasov
8ccc996e63 PreviewHandler: change install to per machine (#2320) 2020-04-23 17:33:56 +03:00
stefansjfw
bd7150d676 Use HKEY_CLASSES_ROOT for powertoys_toast_clsid (#2069)
* Use –HKEY_CLASSES_ROOT for powertoys_toast_clsid

* ImageResizer fix
2020-04-23 16:31:35 +02:00
Clint Rutkas
48d744b6bd updated app icons (#2289) 2020-04-21 11:02:39 -07:00
Clint Rutkas
6eba8130ba Adding community page (#2285)
* first pass for community

* tweak in language

* added in Raf's WinCPP upgrade
2020-04-21 11:02:26 -07:00
vldmr11080
5ac7eddd03 Compare window desktop id with currently active work area desktop id. (#2110)
* Compare window desktop id with currently active work area desktop id.

* Improve error handling and conform to coding guidelines.

* Move virtual desktop helper functions to ZoneWindowUtils namespace.

* Ensure thread safety when creating instance of VirtualDesktopManager.

* Remove static qualifier from ServiceProvider.

* Return instead of break, as there is no need to check for other monitors, virtual desktop is the same for all.

* Move virtual desktop related helper functions to separate files.

* Skip comparing desktop ids if zone window has empty GUID for desktop id.

* Add comment describion scenario for which we need this fix.
2020-04-21 19:57:21 +02:00
Andrey Nekrasov
b5dfe6320d msi: detect and set previous installation path via a custom action (#2108) 2020-04-21 20:52:28 +03:00
Andrey Nekrasov
0354026292 runner: initial automatic update (#2141) 2020-04-21 10:30:12 +03:00
Mattwmaster58
e9ecdb3f56 update github releases to latest link (#2223) 2020-04-20 17:18:38 -07:00
PrzemyslawTusinski
dcee505f6b Dpi unaware placement bug - multimontior with same DPI settings fix (#2156)
* Dpi unaware placement bug - multimontior with same DPI settings fix

* Using different enumerating method

* Changed AllMonitorHaveSameDpiScaling method

* Removed accidental file

* small rename

* Changed some methods to CamelCase

* Review comments fixes
2020-04-20 18:09:10 +02:00
Ivan Stošić
af4678ff18 Added a mutex to ZoneWindow, ensured no data races occur (#2154)
* Added a mutex to ZoneWindow, ensured no data races occur

* Protected draggedWindow* members with a mutex

* Ensured that critical reads happen in a single transaction
2020-04-20 16:32:49 +02:00
Seraphima Zykova
cab5a97117 FZ editor: Splitted zones positioning (#2158) 2020-04-20 11:54:25 +03:00
Nghia M. Luong
5cfa8889f4 Fix for issue #1532 - [PowerToys tray icon] Show version on tooltip (#2117)
* Fix for issue #1532

[PowerToys] Show version on tooltip

* Update src/runner/tray_icon.cpp

Co-Authored-By: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>

Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>
2020-04-17 11:47:24 +03:00
eduardodextil
0cbe7a16e3 Implementing changes suggested in #1992 (#2116)
* Implementing changes suggested in #1992

* Update Product.wxs

Co-authored-by: Ebenezer Ewumi <ebenezer.ewumi@wsu.edu>
2020-04-17 11:43:36 +03:00
vldmr11080
aa6cb7d650 Migrate power rename MRU lists from registry to JSON (#2090)
* Handle most recently used search/replace strings within settings.

* Check for last modified time of json file and reload it if needed.

* Handle changes in MRU search / replace lists size.

* Improve handling of changes in MRU list size.

* Don't check for last modified time in every getter method. Load only when starting application.

* Add const identifier to getter methods.

* Address PR comments: Add const to reg and json file paths and set them in constructor initializer. Check pushIdx validity. Move implementation to cpp of PowerRenameUI constructor.

* Add error checking when getting values from registry.
2020-04-15 23:14:17 +02:00
Ben Randall
67e6688e69 Move markdown parsing logic outside control thread (#2099)
* Move markdown parsing logic outside control thread

* Update MarkdownPreviewHandlerControl.cs

* Remove trailing whitespace.

That'll teach me for trying to make an edit from the GitHub page.
2020-04-15 13:10:55 -07:00
PrzemyslawTusinski
2077cd4864 Dpi unaware placement bug (#2121)
Fix for bug when placing dpi unaware window such as Notepad++ in left of right part of monitor. In that application gap of about 7px was left or right.
This fixes only single-monitor scenario
It skips correction for dpi unaware window that leaves a gap
2020-04-15 12:34:11 +02:00
PrzemyslawTusinski
87fb6fc3d1 Added unit test for non-resizable window placement (#2017) 2020-04-14 10:40:30 +02:00
Clint Rutkas
d584dc5632 Update NOTICE.md 2020-04-13 10:48:05 -07:00
Andrey Nekrasov
86704efcec FancyZones: optimize elevation detection logic (#2103) 2020-04-13 18:22:37 +03:00
Andrey Nekrasov
6bb0f18d53 chore: format PowerToys custom actions (#2104)
* chore: format PowerToys custom actions

* add curly braces
2020-04-13 16:00:51 +03:00
yuyoyuppe
77e4984468 chore: fix window walker warning 2020-04-11 11:24:33 +03:00
yuyoyuppe
4e18c67ec5 chore: fix linking warnings 2020-04-11 11:24:33 +03:00
Clint Rutkas
41ab94fe13 updated notice (#2075) 2020-04-10 12:39:25 -07:00
Seraphima Zykova
14441ec144 WinAppDriver tests fix (#2006)
* updated wait methods and launch
* canvas zone resize tests updated
* updated editor opening
2020-04-10 18:52:16 +03:00
stefansjfw
f589dd2f26 Only clone layout from parent desktop when creating new virtual desktop (#1904)
* Fix issue #1343

* Add Unit Tests

* Revert non intended rename

* Address PR comments
2020-04-10 16:29:18 +02:00
Ivan Stošić
629ba763d7 Basic support for snapping to multiple zones (#1955)
* Refactor a method which resizes windows

* Completed initial work for MultiZones

Without changing any test, they all pass!

* Implemented a basic version of Multizones, updated some tests

* Reduced the sensitivity radius

* Added a few must-have unit tests for Multizones

* Some fixups

* Took care of the conflict between this and #1938

* Improved how zones are detected, reverted a change in one unit test

* Resolved another merge conflict

* Fixed bugs related to stamping
2020-04-10 16:09:08 +02:00
Clint Rutkas
e896e1b3dd fixing a bunch of warnings from Window Walker (#2014)
* first set of warning fixes

* Since this is a Interop item, it should have a _

* Update Window.cs

* Update InteropAndHelpers.cs

looks like there was a space a the top

* Update Window.cs
2020-04-10 06:41:19 -07:00
Yevhenii Holovachov
b92a127200 Added icon for FZEditor (#1388)
* Added icon for FZEditor
2020-04-10 12:15:59 +03:00
Jaime Bernardo
6419c6b1ba settings: fix minimist package vulnerable version (#2021)
Updates nested dependencies so that a vulnerable version of minimist
is not being used.
2020-04-09 17:20:17 +02:00
Seraphima Zykova
1f9d9c619e FZ editor: splitter thickness set to 1px when space around zones is zero (#2020) 2020-04-09 16:47:25 +03:00
vldmr11080
c355a2b61e Use JSON data file for storing PowerRename settings instead of registry (#1909)
* Use JSON data file for storing PowerRename settings instead of registry

* Address PR comments and made several improvements

* Remove WindowsApp.lib dependencies in test app and unit tests

* Revert changes in vcxproj for unit test

* Solve linker warnings generated while linking WindowsApp.lib

* Don't migrate enabled flag. Always read / write from registry.
2020-04-08 19:12:46 +02:00
PrzemyslawTusinski
17022d50d4 Fancy zones preserve size of non-resizable windows (#1974)
* fancy zones preserve size of non-resizable windows

* Changed the method of determining if window is resizable

* minor merge fix
2020-04-08 18:22:43 +02:00
PrzemyslawTusinski
fa515604dc MoveWindowIntoZoneByIndexWithInvalidIndex failing TC fix (#2009) 2020-04-08 18:01:51 +02:00
Seraphima Zykova
efb86f453c Draw border inside zone (#2000) 2020-04-08 16:59:17 +03:00
stefansjfw
569c18c9f1 Remove uneeded TmpMigrateAppliedZoneSets() (#1850)
This was needed while we were migratin device-info so
active layout could be migrated as well. At some point
we dropped migration of device-info. Therefore, this has no
puprose anymore.
2020-04-08 15:43:19 +02:00
Yevhenii Holovachov
91223a8431 Tests: Editor settings tests (#1682)
* Added tests for padding checkbox, padding values
2020-04-08 16:33:05 +03:00
PrzemyslawTusinski
d95e49b535 Dragging by upper part of application bar doesn't trigger fancy zones display - fix (#1953)
* Dragging by upper part of application bar doesn't trigger fancy zones - fix

* reversed if logic
2020-04-08 15:12:04 +02:00
Niels Laute
4cb89b3fc2 FZ - Styling updates to GridZone and CanvasZone controls so they look more Fluent (#1778)
* Improved styling so it looks more Fluent and native to W10

* CanvasZone border is now using the active accent color

* Updated GridZone to a similiar Fluent styling as the CanvasZone. Added a mouseover state to the GridResizer.
2020-04-08 14:46:05 +02:00
Andrey Nekrasov
5e56c66201 msi: fix icon bug due to wix quirk (#2001) 2020-04-08 13:19:58 +03:00
Betsegaw (Beta) Tadele
cb454734b0 [Window Walker] Handle UWP process names (#1858)
* Fixed UWP app search

* Use new methods

* Updated comments
2020-04-07 15:22:17 -07:00
Arjun Balgovind
4f29f998c5 Fixed telemetry calls in Image Resizer (#1983) 2020-04-07 15:19:22 -07:00
vhanla
c241d48df3 Better app listing like Alt-Tab supports UWP apps and hidden apps running including in another Virtual Desktops (#1956)
* Better app listing like Alt-Tab supports UWP apps and hidden apps running in another virtual desktop just like Windows Alt-Tab

* Modified not to hide cloaked apps i.e. also show apps running in another virtual desktop

* Improved listing apps, with readability and reusable abstract properties.
Including a function to detect cloaked UWP apps for future use.
2020-04-07 15:02:14 -07:00
Seraphima Zykova
5ff146bc69 KeePass window showing from tray (#1938) 2020-04-07 19:30:12 +03:00
Yevhenii Holovachov
de42d9f310 Opening settings when user is opening already running Powertoys (#1957)
* Added code to open general settings when opening second instance of powertoys
2020-04-07 13:17:18 +03:00
Seraphima Zykova
f5ed9f1c33 Editor: fixed zone offset scaling (#1876) 2020-04-07 12:03:57 +03:00
JordanAnthonyKing
01ec472314 Issue #1849: Fix for windows in the last zone being placed in the first zone when decreasing the zone count. (#1884) 2020-04-07 10:23:59 +02:00
Enrico Giordani
84ef245b56 Now working on 0.16.2 (#1959) 2020-04-06 21:38:04 +02:00
141 changed files with 10733 additions and 2107 deletions

View File

@@ -46,6 +46,7 @@ build:
include:
- 'PowerToys.exe'
- 'PowerToysSettings.exe'
- 'action_runner.exe'
- 'modules\FancyZonesEditor.exe'
- 'modules\fancyzones.dll'
- 'modules\shortcut_guide.dll'

117
NOTICE.md
View File

@@ -1,28 +1,12 @@
# NOTICES AND INFORMATION
Do Not Translate or Localize
This software incorporates material from third parties.
This software incorporates material from third parties. Microsoft makes certain
open source code available at http://3rdpartysource.microsoft.com, or you may
send a check or money order for US $5.00, including the product name, the open
source component name, and version number, to:
## PowerToy: ImageResizer
```
Source Code Compliance Team
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052
USA
```
Notwithstanding any other terms, you may reverse engineer this software to the
extent required to debug changes to any libraries licensed under the GNU Lesser
General Public License.
## ImageResizer
### Brice Lams's Image Resizer License
**Source**: https://github.com/bricelam/ImageResizer/
### License
The MIT License (MIT)
Copyright (c) Brice Lambson. All rights reserved.
@@ -44,3 +28,98 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## PowerToy: Launcher
### Wox License
**Source**: https://github.com/Wox-launcher/Wox
The MIT License (MIT)
Copyright (c) 2015 Wox
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### Beta Tadele's Window Walker License
**Source**: https://github.com/betsegaw/windowwalker
The MIT License (MIT)
Copyright 2020 Betsegaw Tadele
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### Squirrel.Windows License
**Source**: https://github.com/Squirrel/Squirrel.Windows/
The MIT License (MIT)
Copyright (c) 2012 GitHub, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
## PowerToy: PowerRename
### Chris Davis's SmartRename License
**Source**: https://github.com/chrdavis/SmartRename
MIT License
Copyright (c) 2017 Chris Davis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -151,7 +151,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msi_to_msix_upgrade_lib", "src\common\msi_to_msix_upgrade_lib\msi_to_msix_upgrade_lib.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updating", "src\common\updating\updating.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}"
EndProject

View File

@@ -13,7 +13,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
### Via Github with MSI [Recommended]
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.16.0-x64.msi` to download the PowerToys installer.
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.16.1-x64.msi` to download the PowerToys installer.
This is our preferred method.

13
community.md Normal file
View File

@@ -0,0 +1,13 @@
# Community
The PowerToys team is extremely grateful to have the support of an amazing active community. The work you do is incredibly important. PowerToys wouldnt be near what it is without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thanks and to recognize your work. This is a living document dedicated to highlighting the high impact community members and their contributions.
## High impact community members
### [@Niels9001](https://github.com/niels9001/) - [Niels Laute](https://nielslaute.com/)
Niels has helped drive large sums of our update toward a new [consistent and modern UX](https://github.com/microsoft/PowerToys/issues/891). This includes the [launcher work](https://github.com/microsoft/PowerToys/issues/44) and [icon design](https://github.com/microsoft/PowerToys/issues/1118).
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -3,25 +3,17 @@
This file captures the prioritized list of issues the FancyZones team will tackle
## On deck
Implement multi-mon editor support [195](https://github.com/microsoft/PowerToys/issues/195)
Add telemetry to new editor [196](https://github.com/microsoft/PowerToys/issues/196)
## Backlog
Add tests to the new editor [197](https://github.com/microsoft/PowerToys/issues/197)
Hitting Esc while dragging should cancel the drag and not move the window into a zone [170](https://github.com/microsoft/PowerToys/issues/170)
Flash Zones is wayyyy too slow with multiple large monitors [167](https://github.com/microsoft/PowerToys/issues/167)
Cycle through windows in a Zone [175](https://github.com/microsoft/PowerToys/issues/175)
Minimize/restore windows in a zone as a group [174](https://github.com/microsoft/PowerToys/issues/174)
FancyZones should support custom layouts for different "environments" [177](https://github.com/microsoft/PowerToys/issues/177)
Win+arrow works between monitors [161](https://github.com/microsoft/PowerToys/issues/161)
Win+arrow is directional based on zone rect [162](https://github.com/microsoft/PowerToys/issues/162)
Dragging a zoned window should restore size to a checkpointed size instead of current rect [166](https://github.com/microsoft/PowerToys/issues/166)
FancyZones should merge with MTND and include zone moves in the pop-up [178](https://github.com/microsoft/PowerToys/issues/178)
Drag to edge of screen automatically switches virtual desktops [168](https://github.com/microsoft/PowerToys/issues/168)
Different color schemes [165](https://github.com/microsoft/PowerToys/issues/165)
Ensure you can easily see zone while dragging [163](https://github.com/microsoft/PowerToys/issues/163)
Visual updates for Win+Arrow [171](https://github.com/microsoft/PowerToys/issues/171)
Add a CLI for FancyZones [180](https://github.com/microsoft/PowerToys/issues/180)
Add "magnetic dragging and resizing" mode to FancyZones [181](https://github.com/microsoft/PowerToys/issues/181)
Create layout from current windows [159](https://github.com/microsoft/PowerToys/issues/159)
Zone sets that have a dynamic number of zones [160](https://github.com/microsoft/PowerToys/issues/160)

View File

@@ -32,13 +32,15 @@
<![CDATA[(WINDOWSBUILDNUMBER >= 17134)]]>
</Condition>
<Icon Id="powertoys.ico" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
<Property Id="ARPPRODUCTICON" Value="powertoys.ico" />
<Icon Id="powertoys.exe" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
<Property Id="ARPPRODUCTICON" Value="powertoys.exe" />
<Feature Id="CoreFeature" Title="PowerToys" AllowAdvertise="no" Absent="disallow" TypicalDefault="install"
Description="Contains the Shortcut Guide and Fancy Zones features.">
<ComponentGroupRef Id="CoreComponents" />
<ComponentGroupRef Id="ResourcesComponents" />
</Feature>
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLFOLDER]" After="CostFinalize" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<UI>
<UIRef Id="WixUI_PTInstallDir"/>
@@ -68,9 +70,12 @@
<RegistrySearch Id="ExistingExtPath" Root="HKCR" Key="CLSID\{0440049F-D1DC-4E46-B27B-98393D79486B}\InprocServer32" Type="raw"/>
</Property>
<Property Id ="EXISTINGIMAGERESIZERPATH">
<RegistrySearch Id="ExistingImageResizerPath" Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/>
<RegistrySearch Id="ExistingImageResizerPath" Root="HKCR" Key="CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/>
</Property>
<InstallUISequence>
<Custom Action="DetectPrevInstallPath" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetRegisterPowerToysSchTaskParam" Before="RegisterPowerToysSchTask" />
<Custom Action="RegisterPowerToysSchTask" After="InstallFiles">
@@ -163,6 +168,13 @@
DllEntry="TelemetryLogRepairFailCA"
/>
<CustomAction Id="DetectPrevInstallPath"
Return="check"
Impersonate="yes"
BinaryKey="PTCustomActions"
DllEntry="DetectPrevInstallPathCA"
/>
<!-- Close 'PowerToys.exe' before uninstall-->
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
<!-- Restart explorer.exe if we detect existing PowerRenameExt.dll or ImageResizerExt.dll installation -->
@@ -205,17 +217,15 @@
</Directory>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="PowerToys"/>
</Directory>
<Directory Id="ProgramMenuFolder"/>
<Directory Id="DesktopFolder" Name="Desktop"/>
</Directory>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BinX64Dir)">
<Component Id="powertoys_toast_clsid" Win64="yes">
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}">
<RegistryKey Root="HKCR" Key="Software\Classes\CLSID\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}">
<RegistryValue Type="string" Value="PowerToys Toast Notifications Background Activator" />
<RegistryValue Type="string" Key="LocalServer32" Value="[INSTALLFOLDER]PowerToys.exe -ToastActivated" />
<RegistryValue Type="string" Key="LocalServer32" Name="ThreadingModel" Value="Apartment" />
@@ -226,9 +236,8 @@
<Shortcut Id="ApplicationStartMenuShortcut"
Name="PowerToys (Preview)"
Description="PowerToys - Windows system utilities to maximize productivity"
Directory="ApplicationProgramsFolder"
WorkingDirectory="INSTALLFOLDER"
Icon="powertoys.ico"
Directory="ProgramMenuFolder"
Icon="powertoys.exe"
IconIndex="0"
Advertise="yes">
<ShortcutProperty Key="System.AppUserModel.ID" Value="Microsoft.PowerToysWin32"/>
@@ -236,7 +245,7 @@
</Shortcut>
</File>
<RegistryKey Root="HKCR" Key="powertoys" Action="createAndRemoveOnUninstall">
<RegistryKey Root="HKCR" Key="powertoys">
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
<RegistryValue Type="string" Value="URL:PowerToys custom internal URI protocol"/>
<RegistryKey Key="DefaultIcon">
@@ -247,7 +256,7 @@
</RegistryKey>
</RegistryKey>
<RemoveFolder Id="DeleteShortcutFolder" Directory="ApplicationProgramsFolder" On="uninstall" />
</Component>
<Component Id="settings_exe" Guid="A5A461A9-7097-4CBA-9D39-3DBBB6B7B80C" Win64="yes">
@@ -256,6 +265,9 @@
<Component Id="notifications_dll" Guid="23B25EE4-BCA2-45DF-BBCD-82FBDF01C5AB" Win64="yes">
<File Id="Notifications.dll" KeyPath="yes" Checksum="yes" />
</Component>
<Component Id="action_runner_exe" Guid="626ABB17-16F0-4007-9A58-6998724A5E14" Win64="yes">
<File Id="action_runner.exe" KeyPath="yes" Checksum="yes" />
</Component>
<Component Id="License_rtf" Guid="3E5AE43B-CFB4-449B-A346-94CAAFF3312E" Win64="yes">
<File Source="$(var.RepoDir)\installer\License.rtf" Id="License.rtf" KeyPath="yes" />
</Component>
@@ -334,68 +346,68 @@
<File Source="$(var.BinX64Dir)\modules\ImageResizerExt.dll" KeyPath="yes" />
</Component>
<Component Id="Module_ImageResizer_Registry" Guid="8B593E2C-2D9B-4EBC-93F7-A2B69707DAC9" Win64="yes">
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
<RegistryKey Root="HKCR" Key="CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
<RegistryValue Value="[ModulesInstallFolder]ImageResizerExt.dll" Type="string" />
<RegistryValue Name="ThreadingModel" Value="Apartment" Type="string" />
</RegistryKey>
<!-- Registry Key for the drag and drop handler -->
<RegistryValue Root="HKCU"
Key="Software\Classes\Directory\ShellEx\DragDropHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="Directory\ShellEx\DragDropHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<!-- Registry Keys for the context menu handler for each of the following image formats: bmp, dib, gif, jfif, jpe, jpeg, jpg, jxr, png, rle, tif, tiff, wdp -->
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.bmp\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.bmp\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.dib\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.dib\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.gif\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.gif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.jfif\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.jfif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.jpe\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.jpe\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.jpeg\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.jpeg\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.jpg\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.jpg\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.jxr\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.jxr\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.rle\ShellEx\ContextMenuHandlers\ImageResizer"
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.rle\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.tif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.tiff\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCR"
Key="SystemFileAssociations\.wdp\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.tif\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.tiff\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
<RegistryValue Root="HKCU"
Key="Software\Classes\SystemFileAssociations\.wdp\ShellEx\ContextMenuHandlers\ImageResizer"
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
Type="string" />
</Component>
<Component Id="Module_PowerPreview" Guid="FF1700D5-1B07-4E07-9A62-4D206645EEA9" Win64="yes">
<!-- Component to include PowerPreview Module Source dll's -->
@@ -413,7 +425,7 @@
<Component Id="Module_PowerPreview_PerUserRegistry" Guid="CD90ADC0-7CD5-4A62-B0AF-23545C1E6DD3" Win64="yes">
<!-- Added a separate component for Per-User registry changes -->
<!-- Registry Key for Class Registration of Svg Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{ddee2b8a-6807-48a6-bb20-2338174ff779}">
<RegistryKey Root="HKCR" Key="CLSID\{ddee2b8a-6807-48a6-bb20-2338174ff779}">
<RegistryValue Type="string" Value="SvgPreviewHandler.SvgPreviewHandler" />
<RegistryValue Type="string" Name="DisplayName" Value="Svg Preview Handler" />
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
@@ -430,7 +442,7 @@
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[ModulesInstallFolder]SvgPreviewHandler.dll" />
</RegistryKey>
<!-- Registry Key for Class Registration of Markdown Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
<RegistryKey Root="HKCR" Key="CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
<RegistryValue Type="string" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
<RegistryValue Type="string" Name="DisplayName" Value="Markdown Preview Handler" />
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
@@ -447,27 +459,27 @@
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[ModulesInstallFolder]MarkdownPreviewHandler.dll" />
</RegistryKey>
<!-- Registry Key for AppID registration -->
<RegistryKey Root="HKCU" Key="Software\Classes\AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
<RegistryKey Root="HKCR" Key="AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
<RegistryValue Type="expandable" Name="DllSurrogate" Value="%SystemRoot%\system32\prevhost.exe" />
</RegistryKey>
<!-- Add Svg preview handler to preview handlers list -->
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
<RegistryValue Type="string" Name="{ddee2b8a-6807-48a6-bb20-2338174ff779}" Value="Svg Preview Handler" />
</RegistryKey>
<!-- Add Markdown preview handler to preview handlers list -->
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
<RegistryValue Type="string" Name="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" Value="Markdown Preview Handler" />
</RegistryKey>
<!-- Add file type association for Svg Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\.svg\shellex">
<RegistryKey Root="HKCR" Key=".svg\shellex">
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{ddee2b8a-6807-48a6-bb20-2338174ff779}" />
</RegistryKey>
<!-- Add file type association for Markdown Preview Handler -->
<RegistryKey Root="HKCU" Key="Software\Classes\.md\shellex">
<RegistryKey Root="HKCR" Key=".md\shellex">
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" />
</RegistryKey>
<!-- Update Key to use IE11 for prevhost.exe -->
<RegistryKey Root="HKCU" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
<RegistryKey Root="HKLM" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
<RegistryValue Type="integer" Name="prevhost.exe" Value="11000" />
</RegistryKey>
</Component>
@@ -500,7 +512,7 @@
Description="PowerToys - Windows system utilities to maximize productivity"
Target="[!PowerToys.exe]"
WorkingDirectory="INSTALLFOLDER"
Icon="powertoys.ico"
Icon="powertoys.exe"
Directory="DesktopFolder"/>
</Component>
</DirectoryRef>
@@ -510,6 +522,7 @@
<ComponentGroup Id="CoreComponents" Directory="INSTALLFOLDER">
<ComponentRef Id="powertoys_exe" />
<ComponentRef Id="notifications_dll" />
<ComponentRef Id="action_runner_exe" />
<ComponentRef Id="powertoys_toast_clsid" />
<ComponentRef Id="License_rtf" />
<ComponentRef Id="PowerToysSvgs" />

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ LIBRARY "PowerToysSetupCustomActions"
EXPORTS
CreateScheduledTaskCA
DetectPrevInstallPathCA
RemoveScheduledTasksCA
TelemetryLogInstallSuccessCA
TelemetryLogInstallCancelCA

View File

@@ -55,11 +55,13 @@
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -76,11 +78,13 @@
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>

View File

@@ -1,13 +1,23 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <strsafe.h>
#include <msiquery.h>
#include <Msi.h>
// WiX Header Files:
#include <wcautil.h>
#define SECURITY_WIN32
#include <Security.h>
#include <Lmcons.h>
// TODO: reference additional headers your program requires here
#include <comdef.h>
#include <taskschd.h>
#include <iostream>
#include <strutil.h>
#include <string>
#include <optional>
#include <pathcch.h>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Version>0.16.1</Version>
<Version>0.17.0</Version>
<DefineConstants>Version=$(Version);</DefineConstants>
</PropertyGroup>
</Project>

View File

@@ -4,19 +4,24 @@
#include <string_view>
#include <common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h>
#include <common/common.h>
#include <common/updating/updating.h>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Storage.h>
#include <Msi.h>
#include "../runner/tray_icon.h"
#include "../runner/action_runner_utils.h"
int uninstall_msi_action()
{
const auto package_path = get_msi_package_path();
const auto package_path = updating::get_msi_package_path();
if (package_path.empty())
{
return 0;
}
if (!uninstall_msi_version(package_path))
if (!updating::uninstall_msi_version(package_path))
{
return -1;
}
@@ -33,6 +38,90 @@ int uninstall_msi_action()
return 0;
}
namespace fs = std::filesystem;
std::optional<fs::path> copy_self_to_temp_dir()
{
std::error_code error;
auto dst_path = fs::temp_directory_path() / "action_runner.exe";
fs::copy_file(get_module_filename(), dst_path, fs::copy_options::overwrite_existing, error);
if (error)
{
return std::nullopt;
}
return std::move(dst_path);
}
bool install_new_version_stage_1(const bool must_restart = false)
{
std::optional<fs::path> installer;
for (auto path : fs::directory_iterator{ updating::get_pending_updates_path() })
{
if (path.path().native().find(updating::installer_filename_pattern) != std::wstring::npos)
{
installer.emplace(std::move(path));
break;
}
}
if (!installer)
{
return false;
}
if (auto copy_in_temp = copy_self_to_temp_dir())
{
// detect if PT was running
const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr);
const bool launch_powertoys = must_restart || pt_main_window != nullptr;
if (pt_main_window != nullptr)
{
SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
}
std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2_CMDARG };
arguments += L" \"";
arguments += installer->c_str();
arguments += L"\" \"";
arguments += get_module_folderpath();
arguments += L"\" ";
arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT_CMDARG : UPDATE_STAGE2_DONT_START_PT_CMDARG;
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
sei.lpFile = copy_in_temp->c_str();
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = arguments.c_str();
return ShellExecuteExW(&sei) == TRUE;
}
else
{
return false;
}
}
bool install_new_version_stage_2(std::wstring_view installer_path, std::wstring_view install_path, const bool launch_powertoys)
{
if (MsiInstallProductW(installer_path.data(), nullptr) != ERROR_SUCCESS)
{
return false;
}
std::error_code _;
fs::remove(installer_path, _);
if (launch_powertoys)
{
std::wstring new_pt_path{ install_path };
new_pt_path += L"\\PowerToys.exe";
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
sei.lpFile = new_pt_path.c_str();
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = UPDATE_REPORT_SUCCESS;
return ShellExecuteExW(&sei) == TRUE;
}
return true;
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
int nArgs = 0;
@@ -47,6 +136,19 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
return uninstall_msi_action();
}
else if (action == UPDATE_NOW_LAUNCH_STAGE1_CMDARG)
{
return !install_new_version_stage_1();
}
else if (action == UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG)
{
return !install_new_version_stage_1(true);
}
else if (action == UPDATE_NOW_LAUNCH_STAGE2_CMDARG)
{
using namespace std::string_view_literals;
return !install_new_version_stage_2(args[2], args[3], args[4] == std::wstring_view{ UPDATE_STAGE2_RESTART_PT_CMDARG });
}
return 0;
}

View File

@@ -157,12 +157,12 @@
<ProjectReference Include="..\common\common.vcxproj">
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
</ProjectReference>
<ProjectReference Include="..\common\msi_to_msix_upgrade_lib\msi_to_msix_upgrade_lib.vcxproj">
<ProjectReference Include="..\common\updating\updating.vcxproj">
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\runner\msi_to_msix_upgrade.h" />
<ClInclude Include="..\runner\updating.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@@ -27,3 +27,14 @@ VersionHelper::VersionHelper(int major, int minor, int revision) :
revision(revision)
{
}
std::wstring VersionHelper::toWstring() const
{
std::wstring result{ L"v" };
result += std::to_wstring(major);
result += L'.';
result += std::to_wstring(minor);
result += L'.';
result += std::to_wstring(revision);
return result;
}

View File

@@ -13,4 +13,6 @@ struct VersionHelper
int major;
int minor;
int revision;
std::wstring toWstring() const;
};

View File

@@ -364,27 +364,31 @@ WindowState get_window_state(HWND hwnd)
return RESTORED;
}
bool is_process_elevated()
bool is_process_elevated(const bool use_cached_value)
{
HANDLE token = nullptr;
bool elevated = false;
auto detection_func = []() {
HANDLE token = nullptr;
bool elevated = false;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
{
TOKEN_ELEVATION elevation;
DWORD size;
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
{
elevated = (elevation.TokenIsElevated != 0);
TOKEN_ELEVATION elevation;
DWORD size;
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
{
elevated = (elevation.TokenIsElevated != 0);
}
}
}
if (token)
{
CloseHandle(token);
}
if (token)
{
CloseHandle(token);
}
return elevated;
return elevated;
};
static const bool cached_value = detection_func();
return use_cached_value ? cached_value : detection_func();
}
bool drop_elevated_privileges()

View File

@@ -61,7 +61,7 @@ enum WindowState
WindowState get_window_state(HWND hwnd);
// Returns true if the current process is running with elevated privileges
bool is_process_elevated();
bool is_process_elevated(const bool use_cached_value = true);
// Drops the elevated privilages if present
bool drop_elevated_privileges();
@@ -78,7 +78,7 @@ bool run_same_elevation(const std::wstring& file, const std::wstring& params);
// Returns true if the current process is running from administrator account
bool check_user_is_admin();
//Returns true when one or more strings from vector found in string
// Returns true when one or more strings from vector found in string
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
// Get the executable path or module name for modern apps

View File

@@ -162,6 +162,7 @@
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="common.cpp" />
<ClCompile Include="version.cpp" />
<ClCompile Include="VersionHelper.cpp" />
<ClCompile Include="windows_colors.cpp" />
<ClCompile Include="window_helpers.cpp" />

View File

@@ -159,5 +159,11 @@
<ClCompile Include="VersionHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="version.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -1,8 +1,9 @@
#include "pch.h"
#include "common.h"
#include "monitors.h"
#include "common.h"
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs)
{
auto lhs_tuple = std::make_tuple(lhs.rect.left, lhs.rect.right, lhs.rect.top, lhs.rect.bottom);
@@ -10,43 +11,47 @@ bool operator==(const ScreenSize& lhs, const ScreenSize& rhs)
return lhs_tuple == rhs_tuple;
}
static BOOL CALLBACK get_displays_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
static BOOL CALLBACK GetDisplaysEnumCb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
{
MONITORINFOEX monitor_info;
monitor_info.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &monitor_info);
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitor_info.rcWork);
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFOEX);
if (GetMonitorInfo(monitor, &monitorInfo))
{
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitorInfo.rcWork);
}
return true;
};
static BOOL CALLBACK get_displays_enum_cb_with_toolbar(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
static BOOL CALLBACK GetDisplaysEnumCbWithNonWorkingArea(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
{
MONITORINFOEX monitor_info;
monitor_info.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &monitor_info);
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitor_info.rcMonitor);
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFOEX);
if (GetMonitorInfo(monitor, &monitorInfo))
{
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitorInfo.rcMonitor);
}
return true;
};
std::vector<MonitorInfo> MonitorInfo::GetMonitors(bool include_toolbar)
std::vector<MonitorInfo> MonitorInfo::GetMonitors(bool includeNonWorkingArea)
{
std::vector<MonitorInfo> monitors;
EnumDisplayMonitors(NULL, NULL, include_toolbar ? get_displays_enum_cb_with_toolbar : get_displays_enum_cb, reinterpret_cast<LPARAM>(&monitors));
EnumDisplayMonitors(NULL, NULL, includeNonWorkingArea ? GetDisplaysEnumCbWithNonWorkingArea : GetDisplaysEnumCb, reinterpret_cast<LPARAM>(&monitors));
std::sort(begin(monitors), end(monitors), [](const MonitorInfo& lhs, const MonitorInfo& rhs) {
return lhs.rect < rhs.rect;
});
return monitors;
}
static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
static BOOL CALLBACK GetPrimaryDisplayEnumCb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
{
MONITORINFOEX monitor_info;
monitor_info.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &monitor_info);
if (monitor_info.dwFlags & MONITORINFOF_PRIMARY)
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFOEX);
if (GetMonitorInfo(monitor, &monitorInfo) && (monitorInfo.dwFlags & MONITORINFOF_PRIMARY))
{
reinterpret_cast<MonitorInfo*>(data)->handle = monitor;
reinterpret_cast<MonitorInfo*>(data)->rect = monitor_info.rcWork;
reinterpret_cast<MonitorInfo*>(data)->rect = monitorInfo.rcWork;
}
return true;
};
@@ -54,26 +59,6 @@ static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRE
MonitorInfo MonitorInfo::GetPrimaryMonitor()
{
MonitorInfo primary({}, {});
EnumDisplayMonitors(NULL, NULL, get_primary_display_enum_cb, reinterpret_cast<LPARAM>(&primary));
EnumDisplayMonitors(NULL, NULL, GetPrimaryDisplayEnumCb, reinterpret_cast<LPARAM>(&primary));
return primary;
}
MonitorInfo MonitorInfo::GetFromWindow(HWND hwnd)
{
auto monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
return GetFromHandle(monitor);
}
MonitorInfo MonitorInfo::GetFromPoint(POINT p)
{
auto monitor = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
return GetFromHandle(monitor);
}
MonitorInfo MonitorInfo::GetFromHandle(HMONITOR monitor)
{
MONITORINFOEX monitor_info;
monitor_info.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &monitor_info);
return MonitorInfo(monitor, monitor_info.rcWork);
}

View File

@@ -31,15 +31,8 @@ struct MonitorInfo : ScreenSize
HMONITOR handle;
// Returns monitor rects ordered from left to right
static std::vector<MonitorInfo> GetMonitors(bool include_toolbar);
// Return primary display
static std::vector<MonitorInfo> GetMonitors(bool includeNonWorkingArea);
static MonitorInfo GetPrimaryMonitor();
// Return monitor on which hwnd window is displayed
static MonitorInfo GetFromWindow(HWND hwnd);
// Return monitor nearest to a point
static MonitorInfo GetFromPoint(POINT p);
// Return monitor info given a HMONITOR
static MonitorInfo GetFromHandle(HMONITOR monitor);
};
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs);

View File

@@ -1,157 +0,0 @@
#include "pch.h"
#include "version.h"
#include "msi_to_msix_upgrade.h"
#include <msi.h>
#include <common/common.h>
#include <common/json.h>
#include <common/winstore.h>
#include <common/notifications.h>
#include <MsiQuery.h>
#include <winrt/Windows.Web.Http.h>
#include <winrt/Windows.Web.Http.Headers.h>
#include <winrt/Windows.Management.Deployment.h>
#include "VersionHelper.h"
namespace
{
const wchar_t* POWER_TOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
const wchar_t* DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH = L"delete_previous_powertoys_confirm";
const wchar_t* USER_AGENT = L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
const wchar_t* LATEST_RELEASE_ENDPOINT = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
const wchar_t* MSIX_PACKAGE_NAME = L"Microsoft.PowerToys";
const wchar_t* MSIX_PACKAGE_PUBLISHER = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
}
namespace localized_strings
{
const wchar_t* OFFER_UNINSTALL_MSI = L"We've detected a previous installation of PowerToys. Would you like to remove it?";
const wchar_t* OFFER_UNINSTALL_MSI_TITLE = L"PowerToys: uninstall previous version?";
const wchar_t* UNINSTALLATION_SUCCESS = L"Previous version of PowerToys was uninstalled successfully.";
const wchar_t* UNINSTALLATION_UNKNOWN_ERROR = L"Error: please uninstall the previous version of PowerToys manually.";
}
std::wstring get_msi_package_path()
{
std::wstring package_path;
wchar_t GUID_product_string[39];
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, GUID_product_string); !found)
{
return package_path;
}
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(GUID_product_string); !installed)
{
return package_path;
}
DWORD package_path_size = 0;
if (const bool has_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size); !has_package_path)
{
return package_path;
}
package_path = std::wstring(++package_path_size, L'\0');
if (const bool got_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size); !got_package_path)
{
package_path = {};
return package_path;
}
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
return package_path;
}
bool offer_msi_uninstallation()
{
const auto selection = SHMessageBoxCheckW(nullptr, localized_strings::OFFER_UNINSTALL_MSI, localized_strings::OFFER_UNINSTALL_MSI_TITLE, MB_ICONQUESTION | MB_YESNO, IDNO, DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH);
return selection == IDYES;
}
bool uninstall_msi_version(const std::wstring& package_path)
{
const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL");
if (ERROR_SUCCESS == uninstall_result)
{
notifications::show_toast(localized_strings::UNINSTALLATION_SUCCESS);
return true;
}
else if (auto system_message = get_last_error_message(uninstall_result); system_message.has_value())
{
try
{
notifications::show_toast(*system_message);
}
catch (...)
{
notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR);
}
}
return false;
}
std::future<std::optional<new_version_download_info>> check_for_new_github_release_async()
{
try
{
winrt::Windows::Web::Http::HttpClient client;
auto headers = client.DefaultRequestHeaders();
headers.UserAgent().TryParseAdd(USER_AGENT);
auto response = co_await client.GetAsync(winrt::Windows::Foundation::Uri{ LATEST_RELEASE_ENDPOINT });
(void)response.EnsureSuccessStatusCode();
const auto body = co_await response.Content().ReadAsStringAsync();
auto json_body = json::JsonValue::Parse(body).GetObjectW();
auto new_version = json_body.GetNamedString(L"tag_name");
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
VersionHelper github_version(winrt::to_string(new_version));
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
if (github_version > current_version)
{
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str() };
}
else
{
co_return std::nullopt;
}
}
catch (...)
{
co_return std::nullopt;
}
}
std::future<bool> uninstall_previous_msix_version_async()
{
winrt::Windows::Management::Deployment::PackageManager package_manager;
try
{
auto packages = package_manager.FindPackagesForUser({}, MSIX_PACKAGE_NAME, MSIX_PACKAGE_PUBLISHER);
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
for (auto package : packages)
{
VersionHelper msix_version(package.Id().Version().Major, package.Id().Version().Minor, package.Id().Version().Revision);
if (msix_version < current_version)
{
co_await package_manager.RemovePackageAsync(package.Id().FullName());
co_return true;
}
}
}
catch (...)
{
}
co_return false;
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <future>
#include <winrt/Windows.Foundation.h>
std::wstring get_msi_package_path();
bool uninstall_msi_version(const std::wstring& package_path);
bool offer_msi_uninstallation();
std::future<bool> uninstall_previous_msix_version_async();
struct new_version_download_info
{
winrt::Windows::Foundation::Uri release_page_uri;
std::wstring version_string;
};
std::future<std::optional<new_version_download_info>> check_for_new_github_release_async();

View File

@@ -1,13 +0,0 @@
#pragma once
#ifndef PCH_H
#define PCH_H
#pragma warning (disable: 5205)
#include <winrt/base.h>
#pragma warning (default: 5205)
#include <Windows.h>
#include <MsiQuery.h>
#include <Shlwapi.h>
#endif //PCH_H

View File

@@ -33,6 +33,11 @@ namespace
constexpr std::wstring_view WIN32_AUMID = L"Microsoft.PowerToysWin32";
}
namespace localized_strings
{
constexpr std::wstring_view SNOOZE_BUTTON = L"Snooze";
}
static DWORD loop_thread_id()
{
static const DWORD thread_id = GetCurrentThreadId();
@@ -213,7 +218,14 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
toast_xml += selection_id;
toast_xml += LR"(" type="selection" defaultInput=")";
toast_xml += std::to_wstring(b.durations[0].minutes);
toast_xml += LR"(">)";
toast_xml += L'"';
if (!b.snooze_title.empty())
{
toast_xml += LR"( title=")";
toast_xml += b.snooze_title;
toast_xml += L'"';
}
toast_xml += L'>';
for (const auto& duration : b.durations)
{
toast_xml += LR"(<selection id=")";
@@ -269,7 +281,9 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
toast_xml += selection_id;
toast_xml += '"';
}
toast_xml += LR"( content="" />)";
toast_xml += LR"( content=")";
toast_xml += localized_strings::SNOOZE_BUTTON;
toast_xml += LR"(" />)";
} },
actions[i]);
}

View File

@@ -22,6 +22,7 @@ namespace notifications
struct snooze_button
{
std::wstring snooze_title;
std::vector<snooze_duration> durations;
};

17
src/common/updating/pch.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#ifndef PCH_H
#define PCH_H
#pragma warning(disable : 5205)
#include <winrt/base.h>
#pragma warning(default : 5205)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <MsiQuery.h>
#include <Shlwapi.h>
#include <Shobjidl.h>
#include <Knownfolders.h>
#include <ShlObj_core.h>
#endif //PCH_H

View File

@@ -0,0 +1,287 @@
#include "pch.h"
#include "version.h"
#include "updating.h"
#include <msi.h>
#include <common/common.h>
#include <common/json.h>
#include <common/version.h>
#include <common/settings_helpers.h>
#include <common/winstore.h>
#include <common/notifications.h>
#include <winrt/Windows.Web.Http.h>
#include <winrt/Windows.Web.Http.Headers.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Management.Deployment.h>
#include <winrt/Windows.Networking.Connectivity.h>
#include "VersionHelper.h"
namespace
{
const wchar_t POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
const wchar_t DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH[] = L"delete_previous_powertoys_confirm";
const wchar_t USER_AGENT[] = L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
const wchar_t MSIX_PACKAGE_NAME[] = L"Microsoft.PowerToys";
const wchar_t MSIX_PACKAGE_PUBLISHER[] = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
const wchar_t UPDATE_NOTIFY_TOAST_TAG[] = L"PTUpdateNotifyTag";
const wchar_t UPDATE_READY_TOAST_TAG[] = L"PTUpdateReadyTag";
const size_t MAX_DOWNLOAD_ATTEMPTS = 3;
}
namespace localized_strings
{
const wchar_t OFFER_UNINSTALL_MSI[] = L"We've detected a previous installation of PowerToys. Would you like to remove it?";
const wchar_t OFFER_UNINSTALL_MSI_TITLE[] = L"PowerToys: uninstall previous version?";
const wchar_t UNINSTALLATION_SUCCESS[] = L"Previous version of PowerToys was uninstalled successfully.";
const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually.";
const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.\n";
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now";
const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch";
const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_AGREE[] = L"Visit";
const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days";
}
namespace updating
{
inline winrt::Windows::Web::Http::HttpClient create_http_client()
{
winrt::Windows::Web::Http::HttpClient client;
auto headers = client.DefaultRequestHeaders();
headers.UserAgent().TryParseAdd(USER_AGENT);
return client;
}
std::wstring get_msi_package_path()
{
std::wstring package_path;
wchar_t GUID_product_string[39];
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, GUID_product_string); !found)
{
return package_path;
}
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(GUID_product_string); !installed)
{
return package_path;
}
DWORD package_path_size = 0;
if (const bool has_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size); !has_package_path)
{
return package_path;
}
package_path = std::wstring(++package_path_size, L'\0');
if (const bool got_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size); !got_package_path)
{
package_path = {};
return package_path;
}
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
return package_path;
}
bool offer_msi_uninstallation()
{
const auto selection = SHMessageBoxCheckW(nullptr, localized_strings::OFFER_UNINSTALL_MSI, localized_strings::OFFER_UNINSTALL_MSI_TITLE, MB_ICONQUESTION | MB_YESNO, IDNO, DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH);
return selection == IDYES;
}
bool uninstall_msi_version(const std::wstring& package_path)
{
const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL");
if (ERROR_SUCCESS == uninstall_result)
{
notifications::show_toast(localized_strings::UNINSTALLATION_SUCCESS);
return true;
}
else if (auto system_message = get_last_error_message(uninstall_result); system_message.has_value())
{
try
{
notifications::show_toast(*system_message);
}
catch (...)
{
notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR);
}
}
return false;
}
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async()
{
try
{
auto client = create_http_client();
auto response = co_await client.GetAsync(winrt::Windows::Foundation::Uri{ LATEST_RELEASE_ENDPOINT });
(void)response.EnsureSuccessStatusCode();
const auto body = co_await response.Content().ReadAsStringAsync();
auto json_body = json::JsonValue::Parse(body).GetObjectW();
auto new_version = json_body.GetNamedString(L"tag_name");
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
VersionHelper github_version(winrt::to_string(new_version));
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
if (github_version > current_version)
{
const std::wstring_view required_asset_extension = winstore::running_as_packaged() ? L".msix" : L".msi";
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
constexpr const std::wstring_view required_filename_pattern = updating::installer_filename_pattern;
for (auto asset_elem : json_body.GetNamedArray(L"assets"))
{
auto asset{ asset_elem.GetObjectW() };
std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str();
std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower);
const bool extension_matched = filename_lower.ends_with(required_asset_extension);
const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos;
const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos;
if (extension_matched && architecture_matched && filename_matched)
{
winrt::Windows::Foundation::Uri msi_download_url{ asset.GetNamedString(L"browser_download_url") };
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str(), std::move(msi_download_url), std::move(filename_lower) };
}
}
}
else
{
co_return std::nullopt;
}
}
catch (...)
{
co_return std::nullopt;
}
}
std::future<bool> uninstall_previous_msix_version_async()
{
winrt::Windows::Management::Deployment::PackageManager package_manager;
try
{
auto packages = package_manager.FindPackagesForUser({}, MSIX_PACKAGE_NAME, MSIX_PACKAGE_PUBLISHER);
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
for (auto package : packages)
{
VersionHelper msix_version(package.Id().Version().Major, package.Id().Version().Minor, package.Id().Version().Revision);
if (msix_version < current_version)
{
co_await package_manager.RemovePackageAsync(package.Id().FullName());
co_return true;
}
}
}
catch (...)
{
}
co_return false;
}
bool could_be_costly_connection()
{
using namespace winrt::Windows::Networking::Connectivity;
ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile();
return internetConnectionProfile.IsWwanConnectionProfile();
}
std::filesystem::path get_pending_updates_path()
{
auto path_str{ PTSettingsHelper::get_root_save_folder_location() };
path_str += L"\\Updates";
return { std::move(path_str) };
}
std::future<void> attempt_to_download_installer(const std::filesystem::path& destination, const winrt::Windows::Foundation::Uri& url)
{
namespace storage = winrt::Windows::Storage;
auto client = create_http_client();
auto response = co_await client.GetAsync(url);
(void)response.EnsureSuccessStatusCode();
auto msi_installer_file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(destination.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
co_await response.Content().WriteToStreamAsync(msi_installer_file_stream);
}
std::future<void> try_autoupdate(const bool download_updates_automatically)
{
const auto new_version = co_await get_new_github_version_info_async();
if (!new_version)
{
co_return;
}
using namespace localized_strings;
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
current_version_to_next_version += L" -> ";
current_version_to_next_version += new_version->version_string;
if (download_updates_automatically && !could_be_costly_connection())
{
auto installer_download_dst = get_pending_updates_path();
std::error_code _;
std::filesystem::create_directories(installer_download_dst, _);
installer_download_dst /= new_version->msi_filename;
bool download_success = false;
for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
{
try
{
co_await attempt_to_download_installer(installer_download_dst, new_version->msi_download_url);
download_success = true;
break;
}
catch (...)
{
// reattempt to download or do nothing
}
}
if (!download_success)
{
notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
contents += current_version_to_next_version;
notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } }, std::move(toast_params));
co_return;
}
notifications::toast_params toast_params{ UPDATE_READY_TOAST_TAG, false };
std::wstring new_version_ready{ GITHUB_NEW_VERSION_READY_TO_INSTALL };
new_version_ready += current_version_to_next_version;
notifications::show_toast_with_activations(std::move(new_version_ready),
{},
{ notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" },
notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" },
notifications::snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } },
std::move(toast_params));
}
else
{
notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
contents += current_version_to_next_version;
notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } }, std::move(toast_params));
}
}
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <optional>
#include <string>
#include <future>
#include <filesystem>
#include <winrt/Windows.Foundation.h>
namespace updating
{
std::wstring get_msi_package_path();
bool uninstall_msi_version(const std::wstring& package_path);
bool offer_msi_uninstallation();
std::future<bool> uninstall_previous_msix_version_async();
struct new_version_download_info
{
winrt::Windows::Foundation::Uri release_page_uri;
std::wstring version_string;
winrt::Windows::Foundation::Uri msi_download_url;
std::wstring msi_filename;
};
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async();
std::future<void> try_autoupdate(const bool download_updates_automatically);
std::filesystem::path get_pending_updates_path();
constexpr inline std::wstring_view installer_filename_pattern = L"powertoyssetup";
}

View File

@@ -22,8 +22,9 @@
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{17DA04DF-E393-4397-9CF0-84DABE11032E}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>msitomsixupgradelib</RootNamespace>
<RootNamespace>updating</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>updating</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@@ -171,11 +172,11 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="msi_to_msix_upgrade.h" />
<ClInclude Include="updating.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="msi_to_msix_upgrade.cpp" />
<ClCompile Include="updating.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>

View File

@@ -18,7 +18,7 @@
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="msi_to_msix_upgrade.h">
<ClInclude Include="updating.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@@ -26,7 +26,7 @@
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="msi_to_msix_upgrade.cpp">
<ClCompile Include="updating.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

21
src/common/version.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include "pch.h"
#include "version.h"
version_architecture get_current_architecture()
{
// TODO: detect ARM build with #ifdef
return version_architecture::x64;
}
const wchar_t* get_architecture_string(const version_architecture v)
{
switch (v)
{
case version_architecture::x64:
return L"x64";
case version_architecture::arm:
return L"arm";
default:
throw std::runtime_error("unknown architecture");
}
}

View File

@@ -2,7 +2,7 @@
#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)
#include "Generated Files\version_gen.h"
#define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, 0
@@ -14,3 +14,12 @@
#define COMPANY_NAME "Microsoft Corporation"
#define COPYRIGHT_NOTE "Copyright (C) 2019 Microsoft Corporation"
enum class version_architecture
{
x64,
arm
};
version_architecture get_current_architecture();
const wchar_t* get_architecture_string(const version_architecture);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 KiB

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -1,43 +1,64 @@
# Overview
Fancy Zones is a window manager that is designed to make it easy to arrange and snap windows into efficient layouts for your workflow and also to restore these layouts quickly. Fancy Zones allows the user to define a set of window locations for a desktop that are drag targets for windows. When the user drags a window into a zone, the windows is resized and repositioned to fill that zone.
FancyZones is a window manager that is designed to make it easy to arrange and snap windows into efficient layouts for your workflow and also to restore these layouts quickly. FancyZones allows the user to define a set of window locations for a desktop that are drag targets for windows. When the user drags a window into a zone, the windows is resized and repositioned to fill that zone.
![Fancy Zones](FancyZones.png)
![FancyZones](FancyZones.png)
To get started with Fancy Zones, you need to enable the utility in Power Toys settings and then invoke the Fancy Zones setup UI. There is a button in settings to invoke this UI, or you can press Win+` (note that this shortcut can be changed in the settings dialog) to launch it. When first launched, the UI presents a list of zone layouts that can be quickly adjusted by how many windows are on the monitor. Choosing a layout shows a preview of that layout on the monitor. Pressing the save and close button sets that layout to the monitor.
To get started with FancyZones, you need to enable the utility in PowerToys settings and then invoke the FancyZones editor UI.
There is a button in settings to invoke the editor, or you can press <code>Win+&grave;</code> (note that this shortcut can be changed in the settings dialog) to launch it.
If you have multiple monitors, to edit the zone settings on each monitor, move the mouse to the desired monitor and then press <code>Win+&grave;</code> to launch the editor UI for that monitor, or move the PowerToys settings window to the desired monitor and then launch the editor UI using the `Edit zones` button.
![Fancy Zones Picker](Picker.png)
When first launched, the UI presents a list of zone layouts that can be quickly adjusted by how many windows are on the monitor. Choosing a layout shows a preview of that layout on the monitor. Pressing the save and close button sets that layout to the monitor.
The editor also supports creating and saving custom layouts. This functionality is in the “Custom” tab in the editor UI. There are two ways to create custom zone layouts, window layout and table layout. These can also be thought of as additive and subtractive models. The additive window layout model starts with a blank layout and supports adding zones that can be dragged and resized similar to windows.
![FancyZones Picker](Picker.png)
![Fancy Zones Window Editor Mode](WindowEditor.png)
The editor also supports creating and saving custom layouts. This functionality is in the `Custom` tab in the editor UI.
There are two ways to create custom zone layouts, window layout and table layout. These can also be thought of as additive and subtractive models.
The additive window layout model starts with a blank layout and supports adding zones that can be dragged and resized similar to windows.
![FancyZones Window Editor Mode](WindowEditor.png)
The subtractive table layout model starts with a table layout and allows zones to be created by splitting and merging zones and then resizing the gutter between zones.
To merge two zone, press and hold the left mouse button and drag the mouse until a second zone is selected, then release the button and a popup menu will show up.
![Fancy Zones Table Editor Mode](TableEditor.png)
![FancyZones Table Editor Mode](TableEditor.png)
The backlog for the utility can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/FancyZonesBacklog.md) and the source code is [here](https://github.com/Microsoft/PowerToys/tree/master/src/modules/fancyzones).
# Snapping a window to two zones (EXPERIMENTAL)
If two zones are adjacent, a window can be snapped to the sum of their area (rounded to the minimum rectangle that contains both). When the mouse cursor in near the common edge of two zones, both zones are activated simultaneously, allowing to drop the window on both.
**NOTE:** when snapping a window to two zones, restoring the window when the app is restarted is not supported yet.
![Two Zones Activation](TwoZonesActivation.png)
# Shortcut Keys
| Shortcut | Action |
| ----------- | ----------- |
| Win + ` | Launches editor (this shortcut is editable in the settings dialog) |
| Win+Ctrl+\<Number> | Cycles through saved layouts with the corresponding number of zones |
| Win+Left/Right Arrow | Move focused window between zones (only if Override snap hotkeys setting is enabled, in that case only the `Win+Left Arrow` and `Win+Right Arrow` are overriden, while the `Win+Up Arrow` and `Win+Down Arrow` keep working as usual) |
| Win+Left/Right Arrow | Move focused window between zones (only if `Override Windows Snap hotkeys` setting is turned on, in that case only the `Win+Left Arrow` and `Win+Right Arrow` are overriden, while the `Win+Up Arrow` and `Win+Down Arrow` keep working as usual) |
FancyZones doesn't override the Windows 10 `Win+Shift+Arrow` to quickly move a window to an adjacent monitor.
# Settings
| Setting | Description |
| --------- | ------------- |
| Configure the zone editor hotkey | To change the default hotkey, click on the textbox (it's not necessary to select or delete the text) and then press on the keyboard the desired key combination |
| Enable zones while dragging with the shift key | Toggles between auto-snap mode with the shift key disabling snapping during a drag and manual snap mode where pressing the shift key during a drag enables snapping |
| Override Windows Snap hotkeys (win+arrow) to move between zones | When this option is on and Fancy Zones is running, it overrides two Windows Snap keys: `Win+Left Arrow` and `Win+Right Arrow` |
| Flash zones when the active FancyZones layout changes | Briefly flash the zone layout when the layout changes or a new virtual desktop is activated |
| Keep windows in their zones when the screen resolution changes | After a screen resolution change, if this setting is enabled, Fancy Zones will resize and reposition windows into the zones they were previously in |
| Keep windows in their zones when the active FancyZones layout changes | When this option is on, Fancy Zones will resize and position windows into the new zone layout by maintaining the previous zone number location of each window |
| Override Windows Snap hotkeys (Win+Arrow) to move between zones | When this option is on and FancyZones is running, it overrides two Windows Snap keys: `Win+Left Arrow` and `Win+Right Arrow` |
| Keep windows in their zones when the screen resolution changes | After a screen resolution change, if this setting is enabled, FancyZones will resize and reposition windows into the zones they were previously in |
| Keep windows in their zones when the active FancyZones layout changes | When this option is on, FancyZones will resize and position windows into the new zone layout by maintaining the previous zone number location of each window |
| Keep windows in their zones when the active virtual desktop changes | If an application is pinned to all virtual desktops, this setting will keep that window in the same zone on all desktops |
| Move newly created windows to the last known zone | Automatically move a newly opened window into the last zone location that application was in |
| Follow mouse cursor instead of focus when launching editor in a multi screen environment | When this option is on, the editor hotkey will launch the editor on the monitor where the mouse cursor is, when this option is off, the editor hotkey will launch the editor on monitor where the current active window is |
| Zone Highlight Color (Default #0078D7) | the color that a zone becomes when it is the active drop target during a window drag |
| Follow mouse cursor instead of focus when launching editor in a multi-monitor environment | When this option is on, the editor hotkey will launch the editor on the monitor where the mouse cursor is, when this option is off, the editor hotkey will launch the editor on monitor where the current active window is |
| Show zones on all monitors while dragging a window | By default FancyZones shows only the zones available on the current monitor, this feature may have performance impact when turned on |
| Show zones on all monitors while dragging a window | When the zones are activated, the dragged window is made transparent to improve the zones visibility |
| Zone highlight color (Default #008CFF) | The color that a zone becomes when it is the active drop target during a window drag |
| Zone Inactive color (Default #F5FCFF) | The color that zones become when they are not an active drop during a window drag |
| Zone border color (Default #FFFFFF) | The color of the border of active and inactive zones |
| Zone opacity (%) (Default 50%) | The percentage of opacity of active and inactive zones |
| Exclude applications from snapping to zones | Add the applications name, or part of the name, one per line (e.g., adding `Notepa` will match both `Notepad.exe` and `Notepad++.exe`, to match only `Notepad.exe` add the `.exe` extension) |
![Fancy Zones Settings UI](FancyZonesSettings.png)
![FancyZones Settings UI](FancyZonesSettings1.png)
![FancyZones Settings UI](FancyZonesSettings2.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -12,6 +12,10 @@
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF333333"/>
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#FF1a1a1a"/>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -5,51 +5,113 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d"
Background="LightGray"
Opacity="0.75"
Background="Transparent"
d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="Frame">
<Grid.RowDefinitions>
<RowDefinition Height="8"/>
<RowDefinition Height="16"/>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
<RowDefinition Height="16"/>
<RowDefinition Height="8"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="8"/>
</Grid.ColumnDefinitions>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" Background="Black" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" Background="Black" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" Background="Black" Grid.Row="4" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" Background="Black" Grid.Row="4" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted"/>
<Thumb x:Name="NResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="0" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted"/>
<Thumb x:Name="SResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="5" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted"/>
<Thumb x:Name="WResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted"/>
<Thumb x:Name="EResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="4" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted"/>
<DockPanel Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3">
<Button DockPanel.Dock="Right" Padding="8,0" Click="OnClose">
<Image Source="images/ChromeClose.png" Height="24" Width="24" />
</Button>
<Thumb x:Name="Caption" Cursor="SizeAll" Background="DarkGray" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted"/>
</DockPanel>
<Rectangle Fill="LightGray" Grid.Row="3" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3"/>
<Canvas x:Name="Body" />
<Label Name="LabelID"
<UserControl.Resources>
<Style x:Key="CanvasZoneThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" >
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.15">
<VisualTransition.GeneratedEasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ThumbBorder" Duration="0:0:0.15" Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.6"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Border BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" Background="{StaticResource CanvasZoneBackgroundBrush}" BorderThickness="1">
<Grid x:Name="Frame">
<Grid.RowDefinitions>
<RowDefinition Height="8"/>
<RowDefinition Height="16"/>
<RowDefinition Height="*"/>
<RowDefinition Height="16"/>
<RowDefinition Height="8"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="8"/>
</Grid.ColumnDefinitions>
<Label Name="LabelID"
Content="ID"
Canvas.Left="10"
Canvas.Bottom="10"
FontSize="80"
FontFamily="Segoe UI"
Foreground="Black"
FontSize="64"
FontFamily="Segoe UI Light"
Foreground="White"
Grid.Column="2"
Grid.Row="3"
Grid.Row="2"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center" />
</Grid>
<Thumb x:Name="Caption" Cursor="SizeAll" Background="Transparent" BorderThickness="3" Padding="4" Grid.Column="0" Grid.ColumnSpan="5" Grid.Row="0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NResize" Cursor="SizeNS" BorderThickness="0,3,0,0" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SResize" Cursor="SizeNS" BorderThickness="0,0,0,3" Grid.Row="4" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="WResize" Cursor="SizeWE" BorderThickness="3,0,0,0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="EResize" Cursor="SizeWE" BorderThickness="0,0,3,0" Grid.Column="4" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" BorderThickness="3,3,0,0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" BorderThickness="0,3,3,0" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="3,0,0,3" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,3,3" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Button Content="&#xE894;" BorderThickness="0" ToolTip="Delete zone" Background="Transparent" Foreground="White" FontSize="16" Padding="4" Click="OnClose" Grid.Row="2" Grid.Column="2" FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{DynamicResource CloseButtonStyle}"/>
<Canvas x:Name="Body" />
</Grid>
</Border>
</UserControl>

View File

@@ -84,6 +84,9 @@
<RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisRuleSet>..\..\..\..\codeAnalysis\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>images\FancyZonesEditor.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
@@ -256,5 +259,8 @@
<ItemGroup>
<Resource Include="images\Merge.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="images\FancyZonesEditor.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -222,6 +222,7 @@ namespace FancyZonesEditor
int newChildIndex = AddZone();
double offset = e.Offset;
double space = e.Space;
if (e.Orientation == Orientation.Vertical)
{
@@ -294,22 +295,28 @@ namespace FancyZonesEditor
model.CellChildMap = newCellChildMap;
sourceCol = 0;
double newTotalExtent = ActualWidth - (space * (cols + 1));
for (int col = 0; col < cols; col++)
{
if (col == foundCol)
{
RowColInfo[] split = _colInfo[col].Split(offset);
RowColInfo[] split = _colInfo[col].Split(offset, space);
newColInfo[col] = split[0];
newColPercents[col] = split[0].Percent;
newColInfo[col++] = split[0];
newColPercents[col] = split[1].Percent;
col++;
newColInfo[col] = split[1];
sourceCol++;
newColPercents[col] = split[1].Percent;
}
else
{
newColInfo[col] = _colInfo[sourceCol];
newColInfo[col].RecalculatePercent(newTotalExtent);
newColPercents[col] = model.ColumnPercents[sourceCol];
newColInfo[col] = _colInfo[sourceCol++];
}
sourceCol++;
}
_colInfo = newColInfo;
@@ -389,22 +396,28 @@ namespace FancyZonesEditor
model.CellChildMap = newCellChildMap;
sourceRow = 0;
double newTotalExtent = ActualHeight - (space * (rows + 1));
for (int row = 0; row < rows; row++)
{
if (row == foundRow)
{
RowColInfo[] split = _rowInfo[row].Split(offset);
RowColInfo[] split = _rowInfo[row].Split(offset, space);
newRowInfo[row] = split[0];
newRowPercents[row] = split[0].Percent;
newRowInfo[row++] = split[0];
newRowPercents[row] = split[1].Percent;
row++;
newRowInfo[row] = split[1];
sourceRow++;
newRowPercents[row] = split[1].Percent;
}
else
{
newRowInfo[row] = _rowInfo[sourceRow];
newRowInfo[row].RecalculatePercent(newTotalExtent);
newRowPercents[row] = model.RowPercents[sourceRow];
newRowInfo[row] = _rowInfo[sourceRow++];
}
sourceRow++;
}
_rowInfo = newRowInfo;

View File

@@ -9,10 +9,16 @@
<Thumb.Template>
<ControlTemplate>
<StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48">
<Ellipse Height="48" Width="48" Fill="#0078D7" />
<Ellipse x:Name="BackgroundEllipse" Height="48" Width="48" Fill="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" />
<Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/>
<Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="BackgroundEllipse" Value="0.6"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>

View File

@@ -7,10 +7,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
Background="LightGray"
BorderBrush="DarkGray"
Background="{StaticResource GridZoneBackgroundBrush}"
BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"
BorderThickness="1"
Opacity="0.5"
Opacity="0.8"
mc:Ignorable="d">
<Grid x:Name="Frame">
<Canvas x:Name="Body" />
@@ -23,9 +23,9 @@
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="ID"
FontFamily="Segoe UI"
FontSize="80"
Foreground="Black" />
FontFamily="Segoe UI Light"
FontSize="64"
Foreground="White" />
<!--<TextBlock Margin="2" Text="Shift Key switches direction&#13;Ctrl Key repeats"/>-->
</Grid>
</UserControl>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -45,7 +45,7 @@ namespace FancyZonesEditor
private void OnSelectionChanged()
{
Background = IsSelected ? Brushes.SteelBlue : Brushes.LightGray;
Background = IsSelected ? SystemParameters.WindowGlassBrush : App.Current.Resources["GridZoneBackgroundBrush"] as SolidColorBrush;
}
public bool IsSelected
@@ -60,7 +60,7 @@ namespace FancyZonesEditor
OnSelectionChanged();
_splitter = new Rectangle
{
Fill = Brushes.DarkGray,
Fill = SystemParameters.WindowGlassBrush,
};
Body.Children.Add(_splitter);
@@ -101,7 +101,16 @@ namespace FancyZonesEditor
private int SplitterThickness
{
get { return Math.Max(((App)Application.Current).ZoneSettings.Spacing, 5); }
get
{
Settings settings = ((App)Application.Current).ZoneSettings;
if (!settings.ShowSpacing)
{
return 1;
}
return Math.Max(settings.Spacing, 1);
}
}
private void UpdateSplitter()
@@ -146,7 +155,7 @@ namespace FancyZonesEditor
protected override void OnMouseEnter(MouseEventArgs e)
{
_splitter.Fill = Brushes.DarkGray;
_splitter.Fill = SystemParameters.WindowGlassBrush; // Active Accent color
base.OnMouseEnter(e);
}
@@ -267,8 +276,15 @@ namespace FancyZonesEditor
}
private void DoSplit(Orientation orientation, double offset)
{
Split?.Invoke(this, new SplitEventArgs(orientation, offset));
{
int spacing = 0;
Settings settings = ((App)Application.Current).ZoneSettings;
if (settings.ShowSpacing)
{
spacing = settings.Spacing;
}
Split?.Invoke(this, new SplitEventArgs(orientation, offset, spacing));
}
private void FullSplit_Click(object sender, RoutedEventArgs e)

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -34,13 +34,24 @@ namespace FancyZonesEditor
return Extent;
}
public RowColInfo[] Split(double offset)
public void RecalculatePercent(double newTotalExtent)
{
Percent = (int)(Extent * _multiplier / newTotalExtent);
}
public RowColInfo[] Split(double offset, double space)
{
RowColInfo[] info = new RowColInfo[2];
int newPercent = (int)(Percent * offset / Extent);
info[0] = new RowColInfo(newPercent);
info[1] = new RowColInfo(Percent - newPercent);
double totalExtent = Extent * _multiplier / Percent;
totalExtent -= space;
int percent0 = (int)(offset * _multiplier / totalExtent);
int percent1 = (int)((Extent - space - offset) * _multiplier / totalExtent);
info[0] = new RowColInfo(percent0);
info[1] = new RowColInfo(percent1);
return info;
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
@@ -13,15 +13,18 @@ namespace FancyZonesEditor
{
}
public SplitEventArgs(Orientation orientation, double offset)
public SplitEventArgs(Orientation orientation, double offset, double thickness)
{
Orientation = orientation;
Offset = offset;
Space = thickness;
}
public Orientation Orientation { get; }
public double Offset { get; }
public double Space { get; }
}
public delegate void SplitEventHandler(object sender, SplitEventArgs args);

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -9,6 +9,7 @@
#include "lib/JsonHelpers.h"
#include "lib/ZoneSet.h"
#include "trace.h"
#include "VirtualDesktopUtils.h"
#include <functional>
#include <common/common.h>
@@ -150,6 +151,7 @@ public:
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept;
protected:
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
@@ -180,6 +182,7 @@ private:
};
bool IsInterestingWindow(HWND window) noexcept;
bool IsCursorTypeIndicatingSizeEvent();
void UpdateZoneWindows() noexcept;
void MoveWindowsOnDisplayChange() noexcept;
void UpdateDragState(HWND window, require_write_lock) noexcept;
@@ -347,17 +350,28 @@ FancyZones::VirtualDesktopInitialize() noexcept
IFACEMETHODIMP_(void)
FancyZones::WindowCreated(HWND window) noexcept
{
std::shared_lock readLock(m_lock);
if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window))
{
for (const auto& [monitor, zoneWindow] : m_zoneWindowMap)
{
// WindowCreated is also invoked when a virtual desktop switch occurs, we need a way
// to figure out when that happens to avoid moving windows that should not be moved.
GUID windowDesktopId{};
GUID zoneWindowDesktopId{};
if (VirtualDesktopUtils::GetWindowDesktopId(window, &windowDesktopId) &&
VirtualDesktopUtils::GetZoneWindowDesktopId(zoneWindow.get(), &zoneWindowDesktopId) &&
(windowDesktopId != zoneWindowDesktopId))
{
return;
}
const auto activeZoneSet = zoneWindow->ActiveZoneSet();
if (activeZoneSet)
{
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
wil::unique_cotaskmem_string guidString;
if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString)))
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &guidString)))
{
int zoneIndex = fancyZonesData.GetAppLastZoneIndex(window, zoneWindow->UniqueId(), guidString.get());
if (zoneIndex != -1)
@@ -437,9 +451,6 @@ void FancyZones::ToggleEditor() noexcept
HMONITOR monitor{};
HWND foregroundWindow{};
UINT dpi_x = DPIAware::DEFAULT_DPI;
UINT dpi_y = DPIAware::DEFAULT_DPI;
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings()->use_cursorpos_editor_startupscreen;
POINT currentCursorPos{};
if (use_cursorpos_editor_startupscreen)
@@ -473,24 +484,14 @@ void FancyZones::ToggleEditor() noexcept
} })
.wait();
if (use_cursorpos_editor_startupscreen)
{
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
}
else
{
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
}
auto zoneWindow = iter->second;
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
fancyZonesData.CustomZoneSetsToJsonFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
const auto taskbar_x_offset = MulDiv(mi.rcWork.left - mi.rcMonitor.left, DPIAware::DEFAULT_DPI, dpi_x);
const auto taskbar_y_offset = MulDiv(mi.rcWork.top - mi.rcMonitor.top, DPIAware::DEFAULT_DPI, dpi_y);
// Do not scale window params by the dpi, that will be done in the editor - see LayoutModel.Apply
const auto taskbar_x_offset = mi.rcWork.left - mi.rcMonitor.left;
const auto taskbar_y_offset = mi.rcWork.top - mi.rcMonitor.top;
const auto x = mi.rcMonitor.left + taskbar_x_offset;
const auto y = mi.rcMonitor.top + taskbar_y_offset;
const auto width = mi.rcWork.right - mi.rcWork.left;
@@ -692,7 +693,7 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
const bool flash = false;
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash);
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash, newWorkArea);
if (zoneWindow)
{
m_zoneWindowMap[monitor] = std::move(zoneWindow);
@@ -707,6 +708,12 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
}
void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept
{
std::shared_lock readLock(m_lock);
MoveWindowIntoZoneByIndexSet(window, monitor, { index });
}
void FancyZones::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept
{
std::shared_lock readLock(m_lock);
if (window != m_windowMoveSize)
@@ -718,7 +725,7 @@ void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int in
if (zoneWindow != m_zoneWindowMap.end())
{
const auto& zoneWindowPtr = zoneWindow->second;
zoneWindowPtr->MoveWindowIntoZoneByIndex(window, index);
zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet);
}
}
}
@@ -758,6 +765,33 @@ bool FancyZones::IsInterestingWindow(HWND window) noexcept
return true;
}
bool FancyZones::IsCursorTypeIndicatingSizeEvent()
{
CURSORINFO cursorInfo = { 0 };
cursorInfo.cbSize = sizeof(cursorInfo);
if (::GetCursorInfo(&cursorInfo))
{
if (::LoadCursor(NULL, IDC_SIZENS) == cursorInfo.hCursor)
{
return true;
}
if (::LoadCursor(NULL, IDC_SIZEWE) == cursorInfo.hCursor)
{
return true;
}
if (::LoadCursor(NULL, IDC_SIZENESW) == cursorInfo.hCursor)
{
return true;
}
if (::LoadCursor(NULL, IDC_SIZENWSE) == cursorInfo.hCursor)
{
return true;
}
}
return false;
}
void FancyZones::UpdateZoneWindows() noexcept
{
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
@@ -845,10 +879,8 @@ void FancyZones::UpdateDragState(HWND window, require_write_lock) noexcept
m_dragEnabled = !(shift | mouse);
}
const bool windowElevated = IsProcessOfWindowElevated(window);
static const bool meElevated = is_process_elevated();
static bool warning_shown = false;
if (windowElevated && !meElevated)
if (!is_process_elevated() && IsProcessOfWindowElevated(window))
{
m_dragEnabled = false;
if (!warning_shown && !is_cant_drag_elevated_warning_disabled())
@@ -933,23 +965,10 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
{
// Only enter move/size if the cursor is inside the window rect by a certain padding.
// This prevents resize from triggering zones.
RECT windowRect{};
::GetWindowRect(window, &windowRect);
const auto padding_x = 8;
const auto padding_y = 6;
windowRect.top += padding_y;
windowRect.left += padding_x;
windowRect.right -= padding_x;
windowRect.bottom -= padding_y;
if (PtInRect(&windowRect, ptScreen) == FALSE)
if (IsCursorTypeIndicatingSizeEvent())
{
return;
}
m_inMoveSize = true;
auto iter = m_zoneWindowMap.find(monitor);

View File

@@ -103,6 +103,7 @@
<ClInclude Include="Settings.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="util.h" />
<ClInclude Include="VirtualDesktopUtils.h" />
<ClInclude Include="Zone.h" />
<ClInclude Include="ZoneSet.h" />
<ClInclude Include="ZoneWindow.h" />
@@ -117,6 +118,7 @@
<ClCompile Include="Settings.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="util.cpp" />
<ClCompile Include="VirtualDesktopUtils.cpp" />
<ClCompile Include="Zone.cpp" />
<ClCompile Include="ZoneSet.cpp" />
<ClCompile Include="ZoneWindow.cpp" />

View File

@@ -48,6 +48,9 @@
<ClInclude Include="JsonHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VirtualDesktopUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -77,6 +80,9 @@
<ClCompile Include="util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VirtualDesktopUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="fancyzones.rc">

View File

@@ -604,9 +604,6 @@ namespace JSONHelpers
if (!std::filesystem::exists(jsonFilePath))
{
TmpMigrateAppliedZoneSetsFromRegistry();
// Custom zone sets have to be migrated after applied zone sets!
MigrateCustomZoneSetsFromRegistry();
SaveFancyZonesData();
@@ -639,56 +636,6 @@ namespace JSONHelpers
json::to_file(jsonFilePath, root);
}
void FancyZonesData::TmpMigrateAppliedZoneSetsFromRegistry()
{
std::wregex ex(L"^[0-9]{3,4}_[0-9]{3,4}$");
std::scoped_lock lock{ dataLock };
wchar_t key[256];
StringCchPrintf(key, ARRAYSIZE(key), L"%s", RegistryHelpers::REG_SETTINGS);
HKEY hkey;
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
{
wchar_t resolutionKey[256]{};
DWORD resolutionKeyLength = ARRAYSIZE(resolutionKey);
DWORD i = 0;
while (RegEnumKeyW(hkey, i++, resolutionKey, resolutionKeyLength) == ERROR_SUCCESS)
{
std::wstring resolution{ resolutionKey };
wchar_t appliedZoneSetskey[256];
StringCchPrintf(appliedZoneSetskey, ARRAYSIZE(appliedZoneSetskey), L"%s\\%s", RegistryHelpers::REG_SETTINGS, resolutionKey);
HKEY appliedZoneSetsHkey;
if (std::regex_match(resolution, ex) && RegOpenKeyExW(HKEY_CURRENT_USER, appliedZoneSetskey, 0, KEY_ALL_ACCESS, &appliedZoneSetsHkey) == ERROR_SUCCESS)
{
ZoneSetPersistedDataOLD data;
DWORD dataSize = sizeof(data);
wchar_t value[256]{};
DWORD valueLength = ARRAYSIZE(value);
DWORD i = 0;
while (RegEnumValueW(appliedZoneSetsHkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
{
ZoneSetData appliedZoneSetData;
appliedZoneSetData.type = TypeFromLayoutId(data.LayoutId);
if (appliedZoneSetData.type != ZoneSetLayoutType::Custom)
{
appliedZoneSetData.uuid = std::wstring{ value };
}
else
{
// uuid is changed later to actual uuid when migrating custom zone sets
appliedZoneSetData.uuid = std::to_wstring(data.LayoutId);
}
appliedZoneSetsMap[value] = appliedZoneSetData;
dataSize = sizeof(data);
valueLength = ARRAYSIZE(value);
}
}
resolutionKeyLength = ARRAYSIZE(resolutionKey);
}
}
}
void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
{
std::scoped_lock lock{ dataLock };
@@ -709,29 +656,19 @@ namespace JSONHelpers
zoneSetData.type = static_cast<CustomLayoutType>(data[2]);
// int version = data[0] * 256 + data[1]; - Not used anymore
std::wstring uuid = std::to_wstring(data[3] * 256 + data[4]);
auto it = std::find_if(appliedZoneSetsMap.cbegin(), appliedZoneSetsMap.cend(), [&uuid](std::pair<std::wstring, ZoneSetData> zoneSetMap) {
return zoneSetMap.second.uuid.compare(uuid) == 0;
});
GUID guid;
auto result = CoCreateGuid(&guid);
if (result != S_OK)
{
continue;
}
wil::unique_cotaskmem_string guidString;
if (!SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
{
continue;
}
if (it != appliedZoneSetsMap.cend())
{
uuid = it->first;
}
else
{
GUID guid;
auto result = CoCreateGuid(&guid);
if (result != S_OK)
{
return;
}
wil::unique_cotaskmem_string guidString;
if (SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
{
uuid = guidString.get();
}
}
std::wstring uuid = guidString.get();
switch (zoneSetData.type)
{

View File

@@ -207,12 +207,16 @@ namespace JSONHelpers
#if defined(UNIT_TESTS)
inline void clear_data()
{
appliedZoneSetsMap.clear();
appZoneHistoryMap.clear();
deviceInfoMap.clear();
customZoneSetsMap.clear();
activeDeviceId.clear();
}
inline void SetDeviceInfo(const std::wstring& deviceId, DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
#endif
inline void SetActiveDeviceId(const std::wstring& deviceId)
@@ -254,10 +258,8 @@ namespace JSONHelpers
void SaveFancyZonesData() const;
private:
void TmpMigrateAppliedZoneSetsFromRegistry();
void MigrateCustomZoneSetsFromRegistry();
std::unordered_map<std::wstring, ZoneSetData> appliedZoneSetsMap{};
std::unordered_map<std::wstring, AppZoneHistoryData> appZoneHistoryMap{};
std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{};
std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{};

View File

@@ -0,0 +1,49 @@
#include "pch.h"
#include "VirtualDesktopUtils.h"
namespace VirtualDesktopUtils
{
const CLSID CLSID_ImmersiveShell = { 0xC2F03A33, 0x21F5, 0x47FA, 0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39 };
const wchar_t GUID_EmptyGUID[] = L"{00000000-0000-0000-0000-000000000000}";
IServiceProvider* GetServiceProvider()
{
IServiceProvider* provider{ nullptr };
if (FAILED(CoCreateInstance(CLSID_ImmersiveShell, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(provider), (PVOID*)&provider)))
{
return nullptr;
}
return provider;
}
IVirtualDesktopManager* GetVirtualDesktopManager()
{
IVirtualDesktopManager* manager{ nullptr };
IServiceProvider* serviceProvider = GetServiceProvider();
if (serviceProvider == nullptr || FAILED(serviceProvider->QueryService(__uuidof(manager), &manager)))
{
return nullptr;
}
return manager;
}
bool GetWindowDesktopId(HWND topLevelWindow, GUID* desktopId)
{
static IVirtualDesktopManager* virtualDesktopManager = GetVirtualDesktopManager();
return (virtualDesktopManager != nullptr) &&
SUCCEEDED(virtualDesktopManager->GetWindowDesktopId(topLevelWindow, desktopId));
}
bool GetZoneWindowDesktopId(IZoneWindow* zoneWindow, GUID* desktopId)
{
// Format: <device-id>_<resolution>_<virtual-desktop-id>
std::wstring uniqueId = zoneWindow->UniqueId();
std::wstring virtualDesktopId = uniqueId.substr(uniqueId.rfind('_') + 1);
if (virtualDesktopId == GUID_EmptyGUID)
{
return false;
}
return SUCCEEDED(CLSIDFromString(virtualDesktopId.c_str(), desktopId));
}
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "ZoneWindow.h"
namespace VirtualDesktopUtils
{
bool GetWindowDesktopId(HWND topLevelWindow, GUID* desktopId);
bool GetZoneWindowDesktopId(IZoneWindow* zoneWindow, GUID* desktopId);
}

View File

@@ -1,9 +1,15 @@
#include "pch.h"
#include <Shellscalingapi.h>
#include <common/dpi_aware.h>
#include <common/monitors.h>
#include "Zone.h"
#include "Settings.h"
#include "util.h"
#include "common/monitors.h"
struct Zone : winrt::implements<Zone, IZone>
{
@@ -20,6 +26,7 @@ public:
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
IFACEMETHODIMP_(RECT) ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept;
private:
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
@@ -61,14 +68,52 @@ IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize)
void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
{
// Skip invisible windows
if (!IsWindowVisible(window))
SizeWindowToRect(window, ComputeActualZoneRect(window, zoneWindow));
}
static BOOL CALLBACK saveDisplayToVector(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
{
reinterpret_cast<std::vector<HMONITOR>*>(data)->emplace_back(monitor);
return true;
}
bool allMonitorsHaveSameDpiScaling()
{
std::vector<HMONITOR> monitors;
EnumDisplayMonitors(NULL, NULL, saveDisplayToVector, reinterpret_cast<LPARAM>(&monitors));
if (monitors.size() < 2)
{
return;
return true;
}
UINT firstMonitorDpiX;
UINT firstMonitorDpiY;
if (S_OK != GetDpiForMonitor(monitors[0], MDT_EFFECTIVE_DPI, &firstMonitorDpiX, &firstMonitorDpiY))
{
return false;
}
for (int i = 1; i < monitors.size(); i++)
{
UINT iteratedMonitorDpiX;
UINT iteratedMonitorDpiY;
if (S_OK != GetDpiForMonitor(monitors[i], MDT_EFFECTIVE_DPI, &iteratedMonitorDpiX, &iteratedMonitorDpiY) ||
iteratedMonitorDpiX != firstMonitorDpiX)
{
return false;
}
}
return true;
}
RECT Zone::ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept
{
// Take care of 1px border
RECT zoneRect = m_zoneRect;
RECT newWindowRect = m_zoneRect;
RECT windowRect{};
::GetWindowRect(window, &windowRect);
@@ -77,47 +122,43 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
const auto level = DPIAware::GetAwarenessLevel(GetWindowDpiAwarenessContext(window));
const bool accountForUnawareness = level < DPIAware::PER_MONITOR_AWARE;
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
{
const auto left_margin = frameRect.left - windowRect.left;
const auto right_margin = frameRect.right - windowRect.right;
const auto bottom_margin = frameRect.bottom - windowRect.bottom;
zoneRect.left -= left_margin;
zoneRect.right -= right_margin;
zoneRect.bottom -= bottom_margin;
LONG leftMargin = frameRect.left - windowRect.left;
LONG rightMargin = frameRect.right - windowRect.right;
LONG bottomMargin = frameRect.bottom - windowRect.bottom;
newWindowRect.left -= leftMargin;
newWindowRect.right -= rightMargin;
newWindowRect.bottom -= bottomMargin;
}
// Map to screen coords
MapWindowRect(zoneWindow, nullptr, &zoneRect);
MapWindowRect(zoneWindow, nullptr, &newWindowRect);
MONITORINFO mi{sizeof(mi)};
MONITORINFO mi{ sizeof(mi) };
if (GetMonitorInfoW(MonitorFromWindow(zoneWindow, MONITOR_DEFAULTTONEAREST), &mi))
{
const auto taskbar_left_size = std::abs(mi.rcMonitor.left - mi.rcWork.left);
const auto taskbar_top_size = std::abs(mi.rcMonitor.top - mi.rcWork.top);
OffsetRect(&zoneRect, -taskbar_left_size, -taskbar_top_size);
if (accountForUnawareness)
OffsetRect(&newWindowRect, -taskbar_left_size, -taskbar_top_size);
if (accountForUnawareness && !allMonitorsHaveSameDpiScaling())
{
zoneRect.left = max(mi.rcMonitor.left, zoneRect.left);
zoneRect.right = min(mi.rcMonitor.right - taskbar_left_size, zoneRect.right);
zoneRect.top = max(mi.rcMonitor.top, zoneRect.top);
zoneRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, zoneRect.bottom);
newWindowRect.left = max(mi.rcMonitor.left, newWindowRect.left);
newWindowRect.right = min(mi.rcMonitor.right - taskbar_left_size, newWindowRect.right);
newWindowRect.top = max(mi.rcMonitor.top, newWindowRect.top);
newWindowRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, newWindowRect.bottom);
}
}
WINDOWPLACEMENT placement{};
::GetWindowPlacement(window, &placement);
placement.rcNormalPosition = zoneRect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0)
{
placement.showCmd = SW_RESTORE | SW_SHOWNA;
newWindowRect.right = newWindowRect.left + (windowRect.right - windowRect.left);
newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top);
}
::SetWindowPlacement(window, &placement);
// Do it again, allowing Windows to resize the window and set correct scaling
// This fixes Issue #365
::SetWindowPlacement(window, &placement);
return newWindowRect;
}
void Zone::StampZone(HWND window, bool stamp) noexcept

View File

@@ -42,9 +42,20 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
*/
IFACEMETHOD_(void, SetId)(size_t id) = 0;
/**
* @retirns Zone identifier.
* @returns Zone identifier.
*/
IFACEMETHOD_(size_t, Id)() = 0;
/**
* Compute the coordinates of the rectangle to which a window should be resized.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @returns a RECT structure, describing global coordinates to which a window should be resized
*/
IFACEMETHOD_(RECT, ComputeActualZoneRect)(HWND window, HWND zoneWindow) = 0;
};
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept;

View File

@@ -122,14 +122,16 @@ public:
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
LayoutType() noexcept { return m_config.LayoutType; }
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
ZoneFromPoint(POINT pt) noexcept;
IFACEMETHODIMP_(std::vector<int>)
ZonesFromPoint(POINT pt) noexcept;
IFACEMETHODIMP_(int)
GetZoneIndexFromWindow(HWND window) noexcept;
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>)
GetZones() noexcept { return m_zones; }
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index, bool stampZone) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept;
IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(void)
@@ -162,41 +164,79 @@ IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
return S_OK;
}
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
ZoneSet::ZoneFromPoint(POINT pt) noexcept
IFACEMETHODIMP_(std::vector<int>)
ZoneSet::ZonesFromPoint(POINT pt) noexcept
{
winrt::com_ptr<IZone> smallestKnownZone = nullptr;
// To reduce redundant calculations, we will store the last known zones area.
int smallestKnownZoneArea = INT32_MAX;
for (auto iter = m_zones.rbegin(); iter != m_zones.rend(); iter++)
const int SENSITIVITY_RADIUS = 20;
std::vector<int> capturedZones;
std::vector<int> strictlyCapturedZones;
for (size_t i = 0; i < m_zones.size(); i++)
{
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
auto zone = m_zones[i];
RECT newZoneRect = zone->GetZoneRect();
if (newZoneRect.left < newZoneRect.right && newZoneRect.top < newZoneRect.bottom) // proper zone
{
RECT* newZoneRect = &zone->GetZoneRect();
if (PtInRect(newZoneRect, pt))
if (newZoneRect.left - SENSITIVITY_RADIUS <= pt.x && pt.x <= newZoneRect.right + SENSITIVITY_RADIUS &&
newZoneRect.top - SENSITIVITY_RADIUS <= pt.y && pt.y <= newZoneRect.bottom + SENSITIVITY_RADIUS)
{
if (smallestKnownZone == nullptr)
{
smallestKnownZone = zone;
RECT* r = &smallestKnownZone->GetZoneRect();
smallestKnownZoneArea = (r->right - r->left) * (r->bottom - r->top);
}
else
{
int newZoneArea = (newZoneRect->right - newZoneRect->left) * (newZoneRect->bottom - newZoneRect->top);
if (newZoneArea < smallestKnownZoneArea)
{
smallestKnownZone = zone;
smallestKnownZoneArea = newZoneArea;
}
}
capturedZones.emplace_back(static_cast<int>(i));
}
if (newZoneRect.left <= pt.x && pt.x < newZoneRect.right &&
newZoneRect.top <= pt.y && pt.y < newZoneRect.bottom)
{
strictlyCapturedZones.emplace_back(static_cast<int>(i));
}
}
}
return smallestKnownZone;
// If only one zone is captured, but it's not strictly captured
// don't consider it as captured
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
{
return {};
}
// If captured zones do not overlap, return all of them
// Otherwise, return the smallest one
bool overlap = false;
for (size_t i = 0; i < capturedZones.size(); ++i)
{
for (size_t j = i + 1; j < capturedZones.size(); ++j)
{
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
auto rectJ = m_zones[capturedZones[j]]->GetZoneRect();
if (max(rectI.top, rectJ.top) < min(rectI.bottom, rectJ.bottom) &&
max(rectI.left, rectJ.left) < min(rectI.right, rectJ.right))
{
overlap = true;
i = capturedZones.size() - 1;
break;
}
}
}
if (overlap)
{
size_t smallestIdx = 0;
for (size_t i = 1; i < capturedZones.size(); ++i)
{
auto rectS = m_zones[capturedZones[smallestIdx]]->GetZoneRect();
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
int smallestSize = (rectS.bottom - rectS.top) * (rectS.right - rectS.left);
int iSize = (rectI.bottom - rectI.top) * (rectI.right - rectI.left);
if (iSize <= smallestSize)
{
smallestIdx = i;
}
}
capturedZones = { capturedZones[smallestIdx] };
}
return capturedZones;
}
IFACEMETHODIMP_(int)
@@ -217,7 +257,7 @@ ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
}
IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index, bool stampZone) noexcept
{
if (m_zones.empty())
{
@@ -226,7 +266,7 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
if (index >= int(m_zones.size()))
{
index = 0;
return;
}
while (auto zoneDrop = ZoneFromWindow(window))
@@ -236,7 +276,55 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
if (auto zone = m_zones.at(index))
{
zone->AddWindowToZone(window, windowZone, false);
zone->AddWindowToZone(window, windowZone, stampZone);
}
}
IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept
{
if (m_zones.empty())
{
return;
}
while (auto zoneDrop = ZoneFromWindow(window))
{
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
}
if (indexSet.size() == 1)
{
MoveWindowIntoZoneByIndex(window, windowZone, indexSet[0], stampZone);
return;
}
RECT size;
bool sizeEmpty = true;
for (int index : indexSet)
{
if (index < static_cast<int>(m_zones.size()))
{
RECT newSize = m_zones.at(index)->ComputeActualZoneRect(window, windowZone);
if (!sizeEmpty)
{
size.left = min(size.left, newSize.left);
size.top = min(size.top, newSize.top);
size.right = max(size.right, newSize.right);
size.bottom = max(size.bottom, newSize.bottom);
}
else
{
size = newSize;
sizeEmpty = false;
}
}
}
if (!sizeEmpty)
{
SizeWindowToRect(window, size);
}
}
@@ -306,10 +394,8 @@ ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient)
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
}
if (auto zone = ZoneFromPoint(ptClient))
{
zone->AddWindowToZone(window, zoneWindow, true);
}
auto zones = ZonesFromPoint(ptClient);
MoveWindowIntoZoneByIndexSet(window, zoneWindow, zones, true);
}
IFACEMETHODIMP_(bool)

View File

@@ -24,12 +24,12 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
*/
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
/**
* Get zone from cursor coordinates.
* Get zones from cursor coordinates.
*
* @param pt Cursor coordinates.
* @returns Zone object (defining coordinates of the zone).
* @returns Vector of indices, corresponding to the current set of zones - the zones considered active.
*/
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
IFACEMETHOD_(std::vector<int>, ZonesFromPoint)(POINT pt) = 0;
/**
* Get index of the zone inside zone layout by window assigned to it.
*
@@ -48,8 +48,20 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @param index Zone index within zone layout.
* @param stampZone Whether the window being added to the zone should be stamped.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index, bool stampZone) = 0;
/**
* Assign window to the zones based on the set of zone indices inside zone layout.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @param indexSet The set of zone indices within zone layout.
* @param stampZone Whether the window being added to the zone should be stamped,
in case a single window is to be added.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND zoneWindow, const std::vector<int>& indexSet, bool stampZone) = 0;
/**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
*

View File

@@ -130,7 +130,7 @@ namespace ZoneWindowDrawUtils
Gdiplus::Color fillColor(colorSetting.fillAlpha, GetRValue(colorSetting.fill), GetGValue(colorSetting.fill), GetBValue(colorSetting.fill));
Gdiplus::Color borderColor(colorSetting.borderAlpha, GetRValue(colorSetting.border), GetGValue(colorSetting.border), GetBValue(colorSetting.border));
Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left, zoneRect.bottom - zoneRect.top);
Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left - 1, zoneRect.bottom - zoneRect.top - 1);
Gdiplus::Pen pen(borderColor, static_cast<Gdiplus::REAL>(colorSetting.thickness));
g.FillRectangle(new Gdiplus::SolidBrush(fillColor), rectangle);
@@ -148,7 +148,7 @@ namespace ZoneWindowDrawUtils
COLORREF highlightColor,
int zoneOpacity,
const std::vector<winrt::com_ptr<IZone>>& zones,
const winrt::com_ptr<IZone>& highlightZone,
const std::vector<int>& highlightZones,
bool flashMode,
bool drawHints) noexcept
{
@@ -158,15 +158,22 @@ namespace ZoneWindowDrawUtils
ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 };
ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
std::vector<bool> isHighlighted(zones.size(), false);
for (int x : highlightZones)
{
isHighlighted[x] = true;
}
for (auto iter = zones.begin(); iter != zones.end(); iter++)
{
int zoneId = static_cast<int>(iter - zones.begin());
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
if (!zone)
{
continue;
}
if (zone != highlightZone)
if (!isHighlighted[zoneId])
{
if (flashMode)
{
@@ -182,13 +189,12 @@ namespace ZoneWindowDrawUtils
DrawZone(hdc, colorViewer, zone, zones, flashMode);
}
}
}
if (highlightZone)
{
colorHighlight.fill = highlightColor;
colorHighlight.border = zoneBorderColor;
DrawZone(hdc, colorHighlight, highlightZone, zones, flashMode);
else
{
colorHighlight.fill = highlightColor;
colorHighlight.border = zoneBorderColor;
DrawZone(hdc, colorHighlight, zone, zones, flashMode);
}
}
}
}
@@ -199,7 +205,7 @@ public:
ZoneWindow(HINSTANCE hinstance);
~ZoneWindow();
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones);
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea);
IFACEMETHODIMP MoveSizeEnter(HWND window, bool dragEnabled) noexcept;
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled) noexcept;
@@ -210,6 +216,8 @@ public:
IsDragEnabled() noexcept { return m_dragEnabled; }
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept;
IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(void)
@@ -232,13 +240,13 @@ protected:
private:
void LoadSettings() noexcept;
void InitializeZoneSets(MONITORINFO const& mi) noexcept;
void InitializeZoneSets(bool newWorkArea) noexcept;
void CalculateZoneSet() noexcept;
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
void OnPaint(wil::unique_hdc& hdc) noexcept;
void OnKeyUp(WPARAM wparam) noexcept;
winrt::com_ptr<IZone> ZoneFromPoint(POINT pt) noexcept;
std::vector<int> ZonesFromPoint(POINT pt) noexcept;
void CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept;
void FlashZones() noexcept;
@@ -253,7 +261,7 @@ private:
bool m_dragEnabled{};
winrt::com_ptr<IZoneSet> m_activeZoneSet;
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
winrt::com_ptr<IZone> m_highlightZone;
std::vector<int> m_highlightZone;
WPARAM m_keyLast{};
size_t m_keyCycle{};
static const UINT m_showAnimationDuration = 200; // ms
@@ -284,12 +292,10 @@ ZoneWindow::ZoneWindow(HINSTANCE hinstance)
ZoneWindow::~ZoneWindow()
{
RestoreOrginalTransparency();
Gdiplus::GdiplusShutdown(gdiplusToken);
}
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones)
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea)
{
m_host.copy_from(host);
@@ -308,7 +314,7 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
m_uniqueId = uniqueId;
LoadSettings();
InitializeZoneSets(mi);
InitializeZoneSets(newWorkArea);
m_window = wil::unique_hwnd{
CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP, workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(), nullptr, nullptr, hinstance, this)
@@ -346,8 +352,6 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
if (m_host->isMakeDraggedWindowTransparentActive())
{
RestoreOrginalTransparency();
draggedWindowExstyle = GetWindowLong(window, GWL_EXSTYLE);
draggedWindow = window;
@@ -363,7 +367,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
m_dragEnabled = dragEnabled;
m_windowMoveSize = window;
m_drawHints = true;
m_highlightZone = nullptr;
m_highlightZone = {};
ShowZoneWindow();
return S_OK;
}
@@ -378,13 +382,13 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
if (dragEnabled)
{
auto highlightZone = ZoneFromPoint(ptClient);
auto highlightZone = ZonesFromPoint(ptClient);
redraw = (highlightZone != m_highlightZone);
m_highlightZone = std::move(highlightZone);
}
else if (m_highlightZone)
else if (m_highlightZone.size())
{
m_highlightZone = nullptr;
m_highlightZone = {};
redraw = true;
}
@@ -432,10 +436,16 @@ ZoneWindow::RestoreOrginalTransparency() noexcept
IFACEMETHODIMP_(void)
ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
{
MoveWindowIntoZoneByIndexSet(window, { index });
}
IFACEMETHODIMP_(void)
ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept
{
if (m_activeZoneSet)
{
m_activeZoneSet->MoveWindowIntoZoneByIndex(window, m_window.get(), index);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet, false);
}
}
@@ -518,7 +528,7 @@ ZoneWindow::HideZoneWindow() noexcept
m_keyLast = 0;
m_windowMoveSize = nullptr;
m_drawHints = false;
m_highlightZone = nullptr;
m_highlightZone = {};
}
}
@@ -529,10 +539,10 @@ void ZoneWindow::LoadSettings() noexcept
JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId);
}
void ZoneWindow::InitializeZoneSets(MONITORINFO const& mi) noexcept
void ZoneWindow::InitializeZoneSets(bool newWorkArea) noexcept
{
auto parent = m_host->GetParentZoneWindow(m_monitor);
if (parent)
if (newWorkArea && parent)
{
// Update device info with device info from parent virtual desktop (if empty).
JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parent->UniqueId(), m_uniqueId);
@@ -685,13 +695,13 @@ void ZoneWindow::OnKeyUp(WPARAM wparam) noexcept
}
}
winrt::com_ptr<IZone> ZoneWindow::ZoneFromPoint(POINT pt) noexcept
std::vector<int> ZoneWindow::ZonesFromPoint(POINT pt) noexcept
{
if (m_activeZoneSet)
{
return m_activeZoneSet->ZoneFromPoint(pt);
return m_activeZoneSet->ZonesFromPoint(pt);
}
return nullptr;
return {};
}
void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept
@@ -739,7 +749,7 @@ void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::Inp
{
m_host->MoveWindowsOnActiveZoneSetChange();
}
m_highlightZone = nullptr;
m_highlightZone = {};
}
void ZoneWindow::FlashZones() noexcept
@@ -773,10 +783,10 @@ LRESULT CALLBACK ZoneWindow::s_WndProc(HWND window, UINT message, WPARAM wparam,
DefWindowProc(window, message, wparam, lparam);
}
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones) noexcept
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept
{
auto self = winrt::make_self<ZoneWindow>(hinstance);
if (self->Init(host, hinstance, monitor, uniqueId, flashZones))
if (self->Init(host, hinstance, monitor, uniqueId, flashZones, newWorkArea))
{
return self;
}

View File

@@ -7,6 +7,7 @@ namespace ZoneWindowUtils
const std::wstring& GetActiveZoneSetTmpPath();
const std::wstring& GetAppliedZoneSetTmpPath();
const std::wstring& GetCustomZoneSetsTmpPath();
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId);
}
@@ -53,6 +54,13 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
* @param index Zone index within zone layout.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0;
/**
* Assign window to the zones based on the set of zone indices inside zone layout.
*
* @param window Handle of window which should be assigned to zone.
* @param indexSet The set of zone indices within zone layout.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const std::vector<int>& indexSet) = 0;
/**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
*
@@ -98,4 +106,4 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
};
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
const std::wstring& uniqueId, bool flashZones) noexcept;
const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept;

View File

@@ -108,3 +108,30 @@ void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
monitorInfo = std::move(sortedMonitorInfo);
}
void SizeWindowToRect(HWND window, RECT rect) noexcept
{
WINDOWPLACEMENT placement{};
::GetWindowPlacement(window, &placement);
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
::GetWindowPlacement(window, &placement);
}
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
{
placement.showCmd = SW_RESTORE | SW_SHOWNA;
}
placement.rcNormalPosition = rect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
::SetWindowPlacement(window, &placement);
// Do it again, allowing Windows to resize the window and set correct scaling
// This fixes Issue #365
::SetWindowPlacement(window, &placement);
}

View File

@@ -118,3 +118,4 @@ inline BYTE OpacitySettingToAlpha(int opacity)
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
void SizeWindowToRect(HWND window, RECT rect) noexcept;

View File

@@ -132,8 +132,8 @@ namespace FancyZonesUnitTests
TEST_METHOD (ZoneFromPointEmpty)
{
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
Assert::IsTrue(nullptr == actual);
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneFromPointInner)
@@ -146,9 +146,9 @@ namespace FancyZonesUnitTests
{
for (int j = top + 1; j < bottom; j++)
{
auto actual = m_set->ZoneFromPoint(POINT{ i, j });
Assert::IsTrue(actual != nullptr);
compareZones(expected, actual);
auto actual = m_set->ZonesFromPoint(POINT{ i, j });
Assert::IsTrue(actual.size() == 1);
compareZones(expected, m_set->GetZones()[actual[0]]);
}
}
}
@@ -161,29 +161,29 @@ namespace FancyZonesUnitTests
for (int i = left; i < right; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ i, top });
Assert::IsTrue(actual != nullptr);
compareZones(expected, actual);
auto actual = m_set->ZonesFromPoint(POINT{ i, top });
Assert::IsTrue(actual.size() == 1);
compareZones(expected, m_set->GetZones()[actual[0]]);
}
for (int i = top; i < bottom; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ left, i });
Assert::IsTrue(actual != nullptr);
compareZones(expected, actual);
auto actual = m_set->ZonesFromPoint(POINT{ left, i });
Assert::IsTrue(actual.size() == 1);
compareZones(expected, m_set->GetZones()[actual[0]]);
}
//bottom and right borders considered to be outside
for (int i = left; i < right; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ i, bottom });
Assert::IsTrue(nullptr == actual);
auto actual = m_set->ZonesFromPoint(POINT{ i, bottom });
Assert::IsTrue(actual.size() == 0);
}
for (int i = top; i < bottom; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ right, i });
Assert::IsTrue(nullptr == actual);
auto actual = m_set->ZonesFromPoint(POINT{ right, i });
Assert::IsTrue(actual.size() == 0);
}
}
@@ -193,8 +193,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ left, top, right, bottom });
m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 101, 101 });
Assert::IsTrue(actual == nullptr);
auto actual = m_set->ZonesFromPoint(POINT{ 200, 200 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneFromPointOverlapping)
@@ -208,9 +208,65 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone4 = MakeZone({ 10, 10, 50, 50 });
m_set->AddZone(zone4);
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual != nullptr);
compareZones(zone2, actual);
// zone4 is expected because it's the smallest one, and it's considered to be inside
// since Multizones support
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual.size() == 1);
compareZones(zone4, m_set->GetZones()[actual[0]]);
}
TEST_METHOD (ZoneFromPointMultizoneHorizontal)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 50, 100 });
Assert::IsTrue(actual.size() == 2);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone3, m_set->GetZones()[actual[1]]);
}
TEST_METHOD (ZoneFromPointMultizoneVertical)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 100, 50 });
Assert::IsTrue(actual.size() == 2);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone2, m_set->GetZones()[actual[1]]);
}
TEST_METHOD(ZoneFromPointMultizoneQuad)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 100, 100 });
Assert::IsTrue(actual.size() == 4);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone2, m_set->GetZones()[actual[1]]);
compareZones(zone3, m_set->GetZones()[actual[2]]);
compareZones(zone4, m_set->GetZones()[actual[3]]);
}
TEST_METHOD (ZoneFromPointWithNotNormalizedRect)
@@ -218,8 +274,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ 100, 100, 0, 0 });
m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual == nullptr);
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneFromPointWithZeroRect)
@@ -227,8 +283,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ 0, 0, 0, 0 });
m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual == nullptr);
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneIndexFromWindow)
@@ -316,7 +372,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsTrue(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
@@ -325,7 +381,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveWindowIntoZoneByIndexWithNoZones)
{
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
}
TEST_METHOD (MoveWindowIntoZoneByIndexWithInvalidIndex)
@@ -338,8 +394,8 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100);
Assert::IsTrue(zone1->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
}
@@ -355,17 +411,17 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
Assert::IsTrue(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsTrue(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsTrue(zone3->ContainsWindow(window));
@@ -382,9 +438,9 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
Assert::IsTrue(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
@@ -401,7 +457,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone1);
auto window = Mocks::Window();
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 101, 101 });
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 200, 200 });
Assert::IsFalse(zone1->ContainsWindow(window));
}

View File

@@ -55,6 +55,7 @@ namespace FancyZonesUnitTests
{
const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId";
std::wstringstream m_parentUniqueId;
std::wstringstream m_uniqueId;
HINSTANCE m_hInst{};
@@ -75,6 +76,7 @@ namespace FancyZonesUnitTests
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}";
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty());
@@ -113,7 +115,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
}
void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow)
@@ -129,14 +131,14 @@ namespace FancyZonesUnitTests
public:
TEST_METHOD(CreateZoneWindow)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowNoHinst)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
@@ -144,7 +146,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoHinstFlashZones)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true);
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true, false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
@@ -152,7 +154,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoMonitor)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr);
@@ -160,7 +162,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoMonitorFlashZones)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true, false);
Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr);
@@ -170,7 +172,7 @@ namespace FancyZonesUnitTests
{
// Generate unique id without device id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str());
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
@@ -186,7 +188,7 @@ namespace FancyZonesUnitTests
{
// Generate unique id without virtual desktop id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(m_zoneWindow.get());
@@ -213,7 +215,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@@ -237,7 +239,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@@ -273,7 +275,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@@ -320,7 +322,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@@ -367,7 +369,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@@ -376,9 +378,68 @@ namespace FancyZonesUnitTests
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD (CreateZoneWindowClonedFromParent)
{
using namespace JSONHelpers;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
// newWorkArea = true - zoneWindow will be cloned from parent
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, true);
Assert::IsNotNull(actualZoneWindow->ActiveZoneSet());
const auto actualZoneSet = actualZoneWindow->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)zoneCount, actualZoneSet.size());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
Assert::AreEqual(zoneCount, currentDeviceInfo.zoneCount);
Assert::AreEqual(spacing, currentDeviceInfo.spacing);
Assert::AreEqual(static_cast<int>(type), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD (CreateZoneWindowNotClonedFromParent)
{
using namespace JSONHelpers;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
// newWorkArea = false - zoneWindow won't be cloned from parent
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(actualZoneWindow->ActiveZoneSet());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
// default values
Assert::AreEqual(false, currentDeviceInfo.showSpacing);
Assert::AreEqual(0, currentDeviceInfo.zoneCount);
Assert::AreEqual(0, currentDeviceInfo.spacing);
Assert::AreEqual(std::wstring{ L"null" }, currentDeviceInfo.activeZoneSet.uuid);
Assert::AreEqual(static_cast<int>(ZoneSetLayoutType::Blank), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD(MoveSizeEnter)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
@@ -389,7 +450,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEnterTwice)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = E_INVALIDARG;
@@ -402,7 +463,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdate)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true);
@@ -413,7 +474,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true);
@@ -424,7 +485,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdatePointBigCoordinates)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true);
@@ -445,7 +506,7 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex);
}
@@ -458,7 +519,7 @@ namespace FancyZonesUnitTests
zoneWindow->MoveSizeEnter(window, true);
const auto expected = S_OK;
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -100, -100 });
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
@@ -468,7 +529,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEndDifferentWindows)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto window = Mocks::Window();
m_zoneWindow->MoveSizeEnter(window, true);
@@ -481,7 +542,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEndWindowNotSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = E_INVALIDARG;
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
@@ -501,14 +562,14 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
}
TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@@ -526,7 +587,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@@ -564,7 +625,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window());
@@ -650,5 +711,29 @@ namespace FancyZonesUnitTests
const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex;
Assert::AreEqual(expected, actual);
}
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
int orginalWidth = 450;
int orginalHeight = 550;
SetWindowPos(window, nullptr, 150, 150, orginalWidth, orginalHeight, SWP_SHOWWINDOW);
SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX);
auto zone = MakeZone(RECT{ 50, 50, 300, 300 });
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_LEFT, true);
RECT inZoneRect;
GetWindowRect(window, &inZoneRect);
Assert::AreEqual(orginalWidth, (int)inZoneRect.right - (int) inZoneRect.left);
Assert::AreEqual(orginalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top);
}
};
}

View File

@@ -147,6 +147,7 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu,
if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
{
hr = HRESULT_FROM_WIN32(GetLastError());
Trace::QueryContextMenuError(hr);
}
else
{
@@ -220,12 +221,12 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
HRESULT hr = E_FAIL;
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
{
Trace::InvokedRet(hr);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0))
{
Trace::InvokedRet(hr);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
CAtlFile writePipe(hWritePipe);
@@ -277,12 +278,12 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
delete[] lpszCommandLine;
if (!CloseHandle(processInformation.hProcess))
{
Trace::InvokedRet(hr);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
if (!CloseHandle(processInformation.hThread))
{
Trace::InvokedRet(hr);
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
@@ -322,7 +323,6 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
writePipe.Close();
hr = S_OK;
Trace::InvokedRet(hr);
return hr;
}

View File

@@ -47,3 +47,13 @@ void Trace::InvokedRet(_In_ HRESULT hr) noexcept
TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ImageResizer_QueryContextMenuError",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -8,4 +8,5 @@ public:
static void EnableImageResizer(_In_ bool enabled) noexcept;
static void Invoked() noexcept;
static void InvokedRet(_In_ HRESULT hr) noexcept;
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
};

View File

@@ -48,7 +48,7 @@ HRESULT CPowerRenameMenu::s_CreateInstance(_In_opt_ IUnknown*, _In_ REFIID riid,
HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject* pdtobj, HKEY)
{
// Check if we have disabled ourselves
if (!CSettings::GetEnabled())
if (!CSettingsInstance().GetEnabled())
return E_FAIL;
// Cache the data object to be used later
@@ -60,11 +60,11 @@ HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObjec
HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirst, UINT, UINT uFlags)
{
// Check if we have disabled ourselves
if (!CSettings::GetEnabled())
if (!CSettingsInstance().GetEnabled())
return E_FAIL;
// Check if we should only be on the extended context menu
if (CSettings::GetExtendedContextMenuOnly() && (!(uFlags & CMF_EXTENDEDVERBS)))
if (CSettingsInstance().GetExtendedContextMenuOnly() && (!(uFlags & CMF_EXTENDEDVERBS)))
return E_FAIL;
HRESULT hr = E_UNEXPECTED;
@@ -81,7 +81,7 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
mii.dwTypeData = (PWSTR)menuName;
mii.fState = MFS_ENABLED;
if (CSettings::GetShowIconOnMenu())
if (CSettingsInstance().GetShowIconOnMenu())
{
HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0);
if (hIcon)
@@ -113,7 +113,7 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
{
HRESULT hr = E_FAIL;
if (CSettings::GetEnabled() &&
if (CSettingsInstance().GetEnabled() &&
(IS_INTRESOURCE(pici->lpVerb)) &&
(LOWORD(pici->lpVerb) == 0))
{
@@ -203,7 +203,7 @@ HRESULT __stdcall CPowerRenameMenu::GetTitle(IShellItemArray* /*psiItemArray*/,
HRESULT __stdcall CPowerRenameMenu::GetIcon(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszIcon)
{
if (!CSettings::GetShowIconOnMenu())
if (!CSettingsInstance().GetShowIconOnMenu())
{
*ppszIcon = nullptr;
return E_NOTIMPL;
@@ -229,7 +229,7 @@ HRESULT __stdcall CPowerRenameMenu::GetCanonicalName(GUID* pguidCommandName)
HRESULT __stdcall CPowerRenameMenu::GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState)
{
*pCmdState = CSettings::GetEnabled() ? ECS_ENABLED : ECS_HIDDEN;
*pCmdState = CSettingsInstance().GetEnabled() ? ECS_ENABLED : ECS_HIDDEN;
return S_OK;
}

View File

@@ -162,7 +162,7 @@
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>PowerRenameExt.def</ModuleDefinitionFile>
<DelayLoadDLLs>gdi32.dll;advapi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
<DelayLoadDLLs>gdi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@@ -207,17 +207,17 @@ public:
settings.add_bool_toogle(
L"bool_persist_input",
GET_RESOURCE_STRING(IDS_RESTORE_SEARCH),
CSettings::GetPersistState());
CSettingsInstance().GetPersistState());
settings.add_bool_toogle(
L"bool_mru_enabled",
GET_RESOURCE_STRING(IDS_ENABLE_AUTO),
CSettings::GetMRUEnabled());
CSettingsInstance().GetMRUEnabled());
settings.add_int_spinner(
L"int_max_mru_size",
GET_RESOURCE_STRING(IDS_MAX_ITEMS),
CSettings::GetMaxMRUSize(),
CSettingsInstance().GetMaxMRUSize(),
0,
20,
1);
@@ -225,12 +225,12 @@ public:
settings.add_bool_toogle(
L"bool_show_icon_on_menu",
GET_RESOURCE_STRING(IDS_ICON_CONTEXT_MENU),
CSettings::GetShowIconOnMenu());
CSettingsInstance().GetShowIconOnMenu());
settings.add_bool_toogle(
L"bool_show_extended_menu",
GET_RESOURCE_STRING(IDS_EXTENDED_MENU_INFO),
CSettings::GetExtendedContextMenuOnly());
CSettingsInstance().GetExtendedContextMenuOnly());
return settings.serialize_to_buffer(buffer, buffer_size);
}
@@ -245,11 +245,12 @@ public:
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::from_json_string(config);
CSettings::SetPersistState(values.get_bool_value(L"bool_persist_input").value());
CSettings::SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value());
CSettings::SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value());
CSettings::SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value());
CSettings::SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value());
CSettingsInstance().SetPersistState(values.get_bool_value(L"bool_persist_input").value());
CSettingsInstance().SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value());
CSettingsInstance().SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value());
CSettingsInstance().SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value());
CSettingsInstance().SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value());
CSettingsInstance().Save();
Trace::SettingsChanged();
}
@@ -282,13 +283,14 @@ public:
void init_settings()
{
m_enabled = CSettings::GetEnabled();
m_enabled = CSettingsInstance().GetEnabled();
Trace::EnablePowerRename(m_enabled);
}
void save_settings()
{
CSettings::SetEnabled(m_enabled);
CSettingsInstance().SetEnabled(m_enabled);
CSettingsInstance().Save();
Trace::EnablePowerRename(m_enabled);
}

View File

@@ -85,13 +85,14 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -99,7 +100,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -107,6 +108,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -116,7 +118,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -124,6 +126,7 @@
<SDLCheck>true</SDLCheck>
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@@ -134,7 +137,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>Create</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@@ -143,6 +146,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@@ -1,186 +1,274 @@
#include "stdafx.h"
#include <commctrl.h>
#include "Settings.h"
#include "PowerRenameInterfaces.h"
#include "settings_helpers.h"
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
const wchar_t c_mruSearchRegPath[] = L"SearchMRU";
const wchar_t c_mruReplaceRegPath[] = L"ReplaceMRU";
#include <filesystem>
#include <commctrl.h>
#include <algorithm>
const wchar_t c_enabled[] = L"Enabled";
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
const wchar_t c_persistState[] = L"PersistState";
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
const wchar_t c_flags[] = L"Flags";
const wchar_t c_searchText[] = L"SearchText";
const wchar_t c_replaceText[] = L"ReplaceText";
const wchar_t c_mruEnabled[] = L"MRUEnabled";
const bool c_enabledDefault = true;
const bool c_showIconOnMenuDefault = true;
const bool c_extendedContextMenuOnlyDefaut = false;
const bool c_persistStateDefault = true;
const bool c_mruEnabledDefault = true;
const DWORD c_maxMRUSizeDefault = 10;
const DWORD c_flagsDefault = 0;
bool CSettings::GetEnabled()
namespace
{
return GetRegBoolValue(c_enabled, c_enabledDefault);
}
const wchar_t c_powerRenameDataFilePath[] = L"\\power-rename-settings.json";
const wchar_t c_searchMRUListFilePath[] = L"\\search-mru.json";
const wchar_t c_replaceMRUListFilePath[] = L"\\replace-mru.json";
bool CSettings::SetEnabled(_In_ bool enabled)
{
return SetRegBoolValue(c_enabled, enabled);
}
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
const wchar_t c_mruSearchRegPath[] = L"\\SearchMRU";
const wchar_t c_mruReplaceRegPath[] = L"\\ReplaceMRU";
bool CSettings::GetShowIconOnMenu()
{
return GetRegBoolValue(c_showIconOnMenu, c_showIconOnMenuDefault);
}
const wchar_t c_enabled[] = L"Enabled";
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
const wchar_t c_persistState[] = L"PersistState";
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
const wchar_t c_flags[] = L"Flags";
const wchar_t c_searchText[] = L"SearchText";
const wchar_t c_replaceText[] = L"ReplaceText";
const wchar_t c_mruEnabled[] = L"MRUEnabled";
const wchar_t c_mruList[] = L"MRUList";
const wchar_t c_insertionIdx[] = L"InsertionIdx";
bool CSettings::SetShowIconOnMenu(_In_ bool show)
{
return SetRegBoolValue(c_showIconOnMenu, show);
}
bool CSettings::GetExtendedContextMenuOnly()
{
return GetRegBoolValue(c_extendedContextMenuOnly, c_extendedContextMenuOnlyDefaut);
}
bool CSettings::SetExtendedContextMenuOnly(_In_ bool extendedOnly)
{
return SetRegBoolValue(c_extendedContextMenuOnly, extendedOnly);
}
bool CSettings::GetPersistState()
{
return GetRegBoolValue(c_persistState, c_persistStateDefault);
}
bool CSettings::SetPersistState(_In_ bool persistState)
{
return SetRegBoolValue(c_persistState, persistState);
}
bool CSettings::GetMRUEnabled()
{
return GetRegBoolValue(c_mruEnabled, c_mruEnabledDefault);
}
bool CSettings::SetMRUEnabled(_In_ bool enabled)
{
return SetRegBoolValue(c_mruEnabled, enabled);
}
DWORD CSettings::GetMaxMRUSize()
{
return GetRegDWORDValue(c_maxMRUSize, c_maxMRUSizeDefault);
}
bool CSettings::SetMaxMRUSize(_In_ DWORD maxMRUSize)
{
return SetRegDWORDValue(c_maxMRUSize, maxMRUSize);
}
DWORD CSettings::GetFlags()
{
return GetRegDWORDValue(c_flags, c_flagsDefault);
}
bool CSettings::SetFlags(_In_ DWORD flags)
{
return SetRegDWORDValue(c_flags, flags);
}
bool CSettings::GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
{
return GetRegStringValue(c_searchText, text, cchBuf);
}
bool CSettings::SetSearchText(_In_ PCWSTR text)
{
return SetRegStringValue(c_searchText, text);
}
bool CSettings::GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
{
return GetRegStringValue(c_replaceText, text, cchBuf);
}
bool CSettings::SetReplaceText(_In_ PCWSTR text)
{
return SetRegStringValue(c_replaceText, text);
}
bool CSettings::SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value)
{
DWORD dwValue = value ? 1 : 0;
return SetRegDWORDValue(valueName, dwValue);
}
bool CSettings::GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue)
{
DWORD value = GetRegDWORDValue(valueName, (defaultValue == 0) ? false : true);
return (value == 0) ? false : true;
}
bool CSettings::SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value)
{
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_DWORD, &value, sizeof(value)))));
}
DWORD CSettings::GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue)
{
DWORD retVal = defaultValue;
DWORD type = REG_DWORD;
DWORD dwEnabled = 0;
DWORD cb = sizeof(dwEnabled);
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, &dwEnabled, &cb) == ERROR_SUCCESS)
long GetRegNumber(const std::wstring& valueName, long defaultValue)
{
retVal = dwEnabled;
DWORD type = REG_DWORD;
DWORD data = 0;
DWORD size = sizeof(DWORD);
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, &data, &size) == ERROR_SUCCESS)
{
return data;
}
return defaultValue;
}
return retVal;
}
bool CSettings::SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value)
{
ULONG cb = (DWORD)((wcslen(value) + 1) * sizeof(*value));
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_SZ, (const BYTE*)value, cb))));
}
bool CSettings::GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf)
{
if (cchBuf > 0)
void SetRegNumber(const std::wstring& valueName, long value)
{
SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), REG_DWORD, &value, sizeof(value));
}
bool GetRegBoolean(const std::wstring& valueName, bool defaultValue)
{
DWORD value = GetRegNumber(valueName.c_str(), defaultValue ? 1 : 0);
return (value == 0) ? false : true;
}
void SetRegBoolean(const std::wstring& valueName, bool value)
{
SetRegNumber(valueName, value ? 1 : 0);
}
std::wstring GetRegString(const std::wstring& valueName,const std::wstring& subPath)
{
wchar_t value[CSettings::MAX_INPUT_STRING_LEN];
value[0] = L'\0';
DWORD type = REG_SZ;
DWORD size = CSettings::MAX_INPUT_STRING_LEN * sizeof(wchar_t);
std::wstring completePath = std::wstring(c_rootRegPath) + subPath;
if (SHGetValue(HKEY_CURRENT_USER, completePath.c_str(), valueName.c_str(), &type, value, &size) == ERROR_SUCCESS)
{
return std::wstring(value);
}
return std::wstring{};
}
DWORD type = REG_SZ;
ULONG cb = cchBuf * sizeof(*value);
return (SUCCEEDED(HRESULT_FROM_WIN32(SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, value, &cb) == ERROR_SUCCESS)));
bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime)
{
WIN32_FILE_ATTRIBUTE_DATA attr{};
if (GetFileAttributesExW(filePath.c_str(), GetFileExInfoStandard, &attr))
{
*lpFileTime = attr.ftLastWriteTime;
return true;
}
return false;
}
}
class MRUListHandler
{
public:
MRUListHandler(int size, const std::wstring& filePath, const std::wstring& regPath) :
pushIdx(0),
nextIdx(1),
size(size),
jsonFilePath(PTSettingsHelper::get_module_save_folder_location(L"PowerRename") + filePath),
registryFilePath(regPath)
{
items.resize(size);
Load();
}
typedef int (CALLBACK* MRUCMPPROC)(LPCWSTR, LPCWSTR);
void Push(const std::wstring& data);
bool Next(std::wstring& data);
typedef struct {
DWORD cbSize;
UINT uMax;
UINT fFlags;
HKEY hKey;
LPCTSTR lpszSubKey;
MRUCMPPROC lpfnCompare;
} MRUINFO;
void Reset();
typedef HANDLE (*CreateMRUListFn)(MRUINFO* pmi);
typedef int (*AddMRUStringFn)(HANDLE hMRU, LPCWSTR data);
typedef int (*EnumMRUListFn)(HANDLE hMRU, int nItem, void* lpData, UINT uLen);
typedef int (*FreeMRUListFn)(HANDLE hMRU);
private:
void Load();
void Save();
void MigrateFromRegistry();
json::JsonArray Serialize();
void ParseJson();
bool Exists(const std::wstring& data);
std::vector<std::wstring> items;
long pushIdx;
long nextIdx;
long size;
const std::wstring jsonFilePath;
const std::wstring registryFilePath;
};
void MRUListHandler::Push(const std::wstring& data)
{
if (Exists(data))
{
// TODO: Already existing item should be put on top of MRU list.
return;
}
items[pushIdx] = data;
pushIdx = (pushIdx + 1) % size;
Save();
}
bool MRUListHandler::Next(std::wstring& data)
{
if (nextIdx == size + 1)
{
Reset();
return false;
}
// Go backwards to consume latest items first.
long idx = (pushIdx + size - nextIdx) % size;
if (items[idx].empty())
{
Reset();
return false;
}
data = items[idx];
++nextIdx;
return true;
}
void MRUListHandler::Reset()
{
nextIdx = 1;
}
void MRUListHandler::Load()
{
if (!std::filesystem::exists(jsonFilePath))
{
MigrateFromRegistry();
Save();
}
else
{
ParseJson();
}
}
void MRUListHandler::Save()
{
json::JsonObject jsonData;
jsonData.SetNamedValue(c_maxMRUSize, json::value(size));
jsonData.SetNamedValue(c_insertionIdx, json::value(pushIdx));
jsonData.SetNamedValue(c_mruList, Serialize());
json::to_file(jsonFilePath, jsonData);
}
json::JsonArray MRUListHandler::Serialize()
{
json::JsonArray searchMRU{};
std::wstring data{};
for (const std::wstring& item : items)
{
searchMRU.Append(json::value(item));
}
return searchMRU;
}
void MRUListHandler::MigrateFromRegistry()
{
std::wstring searchListKeys = GetRegString(c_mruList, registryFilePath);
std::sort(std::begin(searchListKeys), std::end(searchListKeys));
for (const wchar_t& key : searchListKeys)
{
Push(GetRegString(std::wstring(1, key), registryFilePath));
}
}
void MRUListHandler::ParseJson()
{
auto json = json::from_file(jsonFilePath);
if (json)
{
const json::JsonObject& jsonObject = json.value();
try
{
long oldSize{ size };
if (json::has(jsonObject, c_maxMRUSize, json::JsonValueType::Number))
{
oldSize = (long)jsonObject.GetNamedNumber(c_maxMRUSize);
}
long oldPushIdx{ 0 };
if (json::has(jsonObject, c_insertionIdx, json::JsonValueType::Number))
{
oldPushIdx = (long)jsonObject.GetNamedNumber(c_insertionIdx);
if (oldPushIdx < 0 || oldPushIdx >= oldSize)
{
oldPushIdx = 0;
}
}
if (json::has(jsonObject, c_mruList, json::JsonValueType::Array))
{
auto jsonArray = jsonObject.GetNamedArray(c_mruList);
if (oldSize == size)
{
for (uint32_t i = 0; i < jsonArray.Size(); ++i)
{
items[i] = std::wstring(jsonArray.GetStringAt(i));
}
pushIdx = oldPushIdx;
}
else
{
std::vector<std::wstring> temp;
for (uint32_t i = 0; i < min(jsonArray.Size(), size); ++i)
{
int idx = (oldPushIdx + oldSize - (i + 1)) % oldSize;
temp.push_back(std::wstring(jsonArray.GetStringAt(idx)));
}
if (size > oldSize)
{
std::reverse(std::begin(temp), std::end(temp));
pushIdx = (long)temp.size();
temp.resize(size);
}
else
{
temp.resize(size);
std::reverse(std::begin(temp), std::end(temp));
}
items = std::move(temp);
Save();
}
}
}
catch (const winrt::hresult_error&) { }
}
}
bool MRUListHandler::Exists(const std::wstring& data)
{
return std::find(std::begin(items), std::end(items), data) != std::end(items);
}
class CRenameMRU :
public IEnumString,
@@ -201,65 +289,33 @@ public:
// IPowerRenameMRU
IFACEMETHODIMP AddMRUString(_In_ PCWSTR entry);
static HRESULT CreateInstance(_In_ PCWSTR regPathMRU, _In_ ULONG maxMRUSize, _Outptr_ IUnknown** ppUnk);
static HRESULT CreateInstance(_In_ const std::wstring& filePath, _In_ const std::wstring& regPath, _Outptr_ IUnknown** ppUnk);
private:
CRenameMRU();
~CRenameMRU();
CRenameMRU(int size, const std::wstring& filePath, const std::wstring& regPath);
HRESULT _Initialize(_In_ PCWSTR regPathMRU, __in ULONG maxMRUSize);
HRESULT _CreateMRUList(_In_ MRUINFO* pmi);
int _AddMRUString(_In_ PCWSTR data);
int _EnumMRUList(_In_ int nItem, _Out_ void* lpData, _In_ UINT uLen);
void _FreeMRUList();
long m_refCount = 0;
HKEY m_hKey = NULL;
ULONG m_maxMRUSize = 0;
ULONG m_mruIndex = 0;
ULONG m_mruSize = 0;
HANDLE m_mruHandle = NULL;
HMODULE m_hComctl32Dll = NULL;
PWSTR m_regPath = nullptr;
std::unique_ptr<MRUListHandler> mruList;
long refCount = 0;
};
CRenameMRU::CRenameMRU() :
m_refCount(1)
{}
CRenameMRU::~CRenameMRU()
CRenameMRU::CRenameMRU(int size, const std::wstring& filePath, const std::wstring& regPath) :
refCount(1)
{
if (m_hKey)
{
RegCloseKey(m_hKey);
}
_FreeMRUList();
if (m_hComctl32Dll)
{
FreeLibrary(m_hComctl32Dll);
}
CoTaskMemFree(m_regPath);
mruList = std::make_unique<MRUListHandler>(size, filePath, regPath);
}
HRESULT CRenameMRU::CreateInstance(_In_ PCWSTR regPathMRU, _In_ ULONG maxMRUSize, _Outptr_ IUnknown** ppUnk)
HRESULT CRenameMRU::CreateInstance(_In_ const std::wstring& filePath, _In_ const std::wstring& regPath, _Outptr_ IUnknown** ppUnk)
{
*ppUnk = nullptr;
HRESULT hr = (regPathMRU && maxMRUSize > 0) ? S_OK : E_FAIL;
long maxMRUSize = CSettingsInstance().GetMaxMRUSize();
HRESULT hr = maxMRUSize > 0 ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
CRenameMRU* renameMRU = new CRenameMRU();
CRenameMRU* renameMRU = new CRenameMRU(maxMRUSize, filePath, regPath);
hr = renameMRU ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = renameMRU->_Initialize(regPathMRU, maxMRUSize);
if (SUCCEEDED(hr))
{
hr = renameMRU->QueryInterface(IID_PPV_ARGS(ppUnk));
}
renameMRU->QueryInterface(IID_PPV_ARGS(ppUnk));
renameMRU->Release();
}
}
@@ -267,21 +323,20 @@ HRESULT CRenameMRU::CreateInstance(_In_ PCWSTR regPathMRU, _In_ ULONG maxMRUSize
return hr;
}
// IUnknown
IFACEMETHODIMP_(ULONG) CRenameMRU::AddRef()
{
return InterlockedIncrement(&m_refCount);
return InterlockedIncrement(&refCount);
}
IFACEMETHODIMP_(ULONG) CRenameMRU::Release()
{
long refCount = InterlockedDecrement(&m_refCount);
long cnt = InterlockedDecrement(&refCount);
if (refCount == 0)
if (cnt == 0)
{
delete this;
}
return refCount;
return cnt;
}
IFACEMETHODIMP CRenameMRU::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
@@ -294,57 +349,8 @@ IFACEMETHODIMP CRenameMRU::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
return QISearch(this, qit, riid, ppv);
}
HRESULT CRenameMRU::_Initialize(_In_ PCWSTR regPathMRU, __in ULONG maxMRUSize)
{
m_maxMRUSize = maxMRUSize;
wchar_t regPath[MAX_PATH] = { 0 };
HRESULT hr = StringCchPrintf(regPath, ARRAYSIZE(regPath), L"%s\\%s", c_rootRegPath, regPathMRU);
if (SUCCEEDED(hr))
{
hr = SHStrDup(regPathMRU, &m_regPath);
if (SUCCEEDED(hr))
{
MRUINFO mi = {
sizeof(MRUINFO),
maxMRUSize,
0,
HKEY_CURRENT_USER,
regPath,
nullptr
};
hr = _CreateMRUList(&mi);
if (SUCCEEDED(hr))
{
m_mruSize = _EnumMRUList(-1, NULL, 0);
}
else
{
hr = E_FAIL;
}
}
}
return hr;
}
// IEnumString
IFACEMETHODIMP CRenameMRU::Reset()
{
m_mruIndex = 0;
return S_OK;
}
#define MAX_ENTRY_STRING 1024
IFACEMETHODIMP CRenameMRU::Next(__in ULONG celt, __out_ecount_part(celt, *pceltFetched) LPOLESTR* rgelt, __out_opt ULONG* pceltFetched)
{
HRESULT hr = S_OK;
WCHAR mruEntry[MAX_ENTRY_STRING];
mruEntry[0] = L'\0';
if (pceltFetched)
{
*pceltFetched = 0;
@@ -360,10 +366,10 @@ IFACEMETHODIMP CRenameMRU::Next(__in ULONG celt, __out_ecount_part(celt, *pceltF
return S_FALSE;
}
hr = S_FALSE;
if (m_mruIndex <= m_mruSize && _EnumMRUList(m_mruIndex++, (void*)mruEntry, ARRAYSIZE(mruEntry)) > 0)
HRESULT hr = S_FALSE;
if (std::wstring data{}; mruList->Next(data))
{
hr = SHStrDup(mruEntry, rgelt);
hr = SHStrDup(data.c_str(), rgelt);
if (SUCCEEDED(hr) && pceltFetched != nullptr)
{
*pceltFetched = 1;
@@ -373,109 +379,143 @@ IFACEMETHODIMP CRenameMRU::Next(__in ULONG celt, __out_ecount_part(celt, *pceltF
return hr;
}
IFACEMETHODIMP CRenameMRU::Reset()
{
mruList->Reset();
return S_OK;
}
IFACEMETHODIMP CRenameMRU::AddMRUString(_In_ PCWSTR entry)
{
return (_AddMRUString(entry) < 0) ? E_FAIL : S_OK;
mruList->Push(entry);
return S_OK;
}
HRESULT CRenameMRU::_CreateMRUList(_In_ MRUINFO* pmi)
CSettings::CSettings()
{
if (m_mruHandle != NULL)
{
_FreeMRUList();
}
if (m_hComctl32Dll == NULL)
{
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
if (m_hComctl32Dll != nullptr)
{
CreateMRUListFn pfnCreateMRUList = reinterpret_cast<CreateMRUListFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(400)));
if (pfnCreateMRUList != nullptr)
{
m_mruHandle = pfnCreateMRUList(pmi);
}
}
return (m_mruHandle != NULL) ? S_OK : E_FAIL;
std::wstring result = PTSettingsHelper::get_module_save_folder_location(L"PowerRename");
jsonFilePath = result + std::wstring(c_powerRenameDataFilePath);
Load();
}
int CRenameMRU::_AddMRUString(_In_ PCWSTR data)
void CSettings::Save()
{
int retVal = -1;
if (m_mruHandle != NULL)
{
if (m_hComctl32Dll == NULL)
{
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
json::JsonObject jsonData;
if (m_hComctl32Dll != nullptr)
jsonData.SetNamedValue(c_enabled, json::value(settings.enabled));
jsonData.SetNamedValue(c_showIconOnMenu, json::value(settings.showIconOnMenu));
jsonData.SetNamedValue(c_extendedContextMenuOnly, json::value(settings.extendedContextMenuOnly));
jsonData.SetNamedValue(c_persistState, json::value(settings.persistState));
jsonData.SetNamedValue(c_mruEnabled, json::value(settings.MRUEnabled));
jsonData.SetNamedValue(c_maxMRUSize, json::value(settings.maxMRUSize));
jsonData.SetNamedValue(c_flags, json::value(settings.flags));
jsonData.SetNamedValue(c_searchText, json::value(settings.searchText));
jsonData.SetNamedValue(c_replaceText, json::value(settings.replaceText));
json::to_file(jsonFilePath, jsonData);
}
void CSettings::Load()
{
if (!std::filesystem::exists(jsonFilePath))
{
MigrateFromRegistry();
Save();
}
else
{
ParseJson();
}
GetSystemTimeAsFileTime(&lastLoadedTime);
}
void CSettings::Reload()
{
// Load json settings from data file if it is modified in the meantime.
FILETIME lastModifiedTime{};
if (LastModifiedTime(jsonFilePath, &lastModifiedTime) &&
CompareFileTime(&lastModifiedTime, &lastLoadedTime) == 1)
{
Load();
}
}
void CSettings::MigrateFromRegistry()
{
settings.enabled = GetRegBoolean(c_enabled, true);
settings.showIconOnMenu = GetRegBoolean(c_showIconOnMenu, true);
settings.extendedContextMenuOnly = GetRegBoolean(c_extendedContextMenuOnly, false); // Disabled by default.
settings.persistState = GetRegBoolean(c_persistState, true);
settings.MRUEnabled = GetRegBoolean(c_mruEnabled, true);
settings.maxMRUSize = GetRegNumber(c_maxMRUSize, 10);
settings.flags = GetRegNumber(c_flags, 0);
settings.searchText = GetRegString(c_searchText, L"");
settings.replaceText = GetRegString(c_replaceText, L"");
}
void CSettings::ParseJson()
{
auto json = json::from_file(jsonFilePath);
if (json)
{
const json::JsonObject& jsonSettings = json.value();
try
{
AddMRUStringFn pfnAddMRUString = reinterpret_cast<AddMRUStringFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(401)));
if (pfnAddMRUString != nullptr)
if (json::has(jsonSettings, c_enabled, json::JsonValueType::Boolean))
{
retVal = pfnAddMRUString(m_mruHandle, data);
settings.enabled = jsonSettings.GetNamedBoolean(c_enabled);
}
if (json::has(jsonSettings, c_showIconOnMenu, json::JsonValueType::Boolean))
{
settings.showIconOnMenu = jsonSettings.GetNamedBoolean(c_showIconOnMenu);
}
if (json::has(jsonSettings, c_extendedContextMenuOnly, json::JsonValueType::Boolean))
{
settings.extendedContextMenuOnly = jsonSettings.GetNamedBoolean(c_extendedContextMenuOnly);
}
if (json::has(jsonSettings, c_persistState, json::JsonValueType::Boolean))
{
settings.persistState = jsonSettings.GetNamedBoolean(c_persistState);
}
if (json::has(jsonSettings, c_mruEnabled, json::JsonValueType::Boolean))
{
settings.MRUEnabled = jsonSettings.GetNamedBoolean(c_mruEnabled);
}
if (json::has(jsonSettings, c_maxMRUSize, json::JsonValueType::Number))
{
settings.maxMRUSize = (long)jsonSettings.GetNamedNumber(c_maxMRUSize);
}
if (json::has(jsonSettings, c_flags, json::JsonValueType::Number))
{
settings.flags = (long)jsonSettings.GetNamedNumber(c_flags);
}
if (json::has(jsonSettings, c_searchText, json::JsonValueType::String))
{
settings.searchText = jsonSettings.GetNamedString(c_searchText);
}
if (json::has(jsonSettings, c_replaceText, json::JsonValueType::String))
{
settings.replaceText = jsonSettings.GetNamedString(c_replaceText);
}
}
catch (const winrt::hresult_error&) { }
}
return retVal;
}
int CRenameMRU::_EnumMRUList(_In_ int nItem, _Out_ void* lpData, _In_ UINT uLen)
CSettings& CSettingsInstance()
{
int retVal = -1;
if (m_mruHandle != NULL)
{
if (m_hComctl32Dll == NULL)
{
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
if (m_hComctl32Dll != nullptr)
{
EnumMRUListFn pfnEnumMRUList = reinterpret_cast<EnumMRUListFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(403)));
if (pfnEnumMRUList != nullptr)
{
retVal = pfnEnumMRUList(m_mruHandle, nItem, lpData, uLen);
}
}
}
return retVal;
}
void CRenameMRU::_FreeMRUList()
{
if (m_mruHandle != NULL)
{
if (m_hComctl32Dll == NULL)
{
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
if (m_hComctl32Dll != nullptr)
{
FreeMRUListFn pfnFreeMRUList = reinterpret_cast<FreeMRUListFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(152)));
if (pfnFreeMRUList != nullptr)
{
pfnFreeMRUList(m_mruHandle);
}
}
m_mruHandle = NULL;
}
static CSettings instance;
return instance;
}
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk)
{
return CRenameMRU::CreateInstance(c_mruSearchRegPath, CSettings::GetMaxMRUSize(), ppUnk);
return CRenameMRU::CreateInstance(c_searchMRUListFilePath, c_mruSearchRegPath, ppUnk);
}
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk)
{
return CRenameMRU::CreateInstance(c_mruReplaceRegPath, CSettings::GetMaxMRUSize(), ppUnk);
return CRenameMRU::CreateInstance(c_replaceMRUListFilePath, c_mruReplaceRegPath, ppUnk);
}

View File

@@ -1,45 +1,136 @@
#pragma once
#include "json.h"
class CSettings
{
public:
static const int MAX_INPUT_STRING_LEN = 1024;
static bool GetEnabled();
static bool SetEnabled(_In_ bool enabled);
CSettings();
static bool GetShowIconOnMenu();
static bool SetShowIconOnMenu(_In_ bool show);
inline bool GetEnabled()
{
Reload();
return settings.enabled;
}
static bool GetExtendedContextMenuOnly();
static bool SetExtendedContextMenuOnly(_In_ bool extendedOnly);
inline void SetEnabled(bool enabled)
{
settings.enabled = enabled;
Save();
}
static bool GetPersistState();
static bool SetPersistState(_In_ bool extendedOnly);
inline bool GetShowIconOnMenu() const
{
return settings.showIconOnMenu;
}
static bool GetMRUEnabled();
static bool SetMRUEnabled(_In_ bool enabled);
inline void SetShowIconOnMenu(bool show)
{
settings.showIconOnMenu = show;
}
static DWORD GetMaxMRUSize();
static bool SetMaxMRUSize(_In_ DWORD maxMRUSize);
inline bool GetExtendedContextMenuOnly() const
{
return settings.extendedContextMenuOnly;
}
static DWORD GetFlags();
static bool SetFlags(_In_ DWORD flags);
inline void SetExtendedContextMenuOnly(bool extendedOnly)
{
settings.extendedContextMenuOnly = extendedOnly;
}
static bool GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf);
static bool SetSearchText(_In_ PCWSTR text);
inline bool GetPersistState() const
{
return settings.persistState;
}
static bool GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf);
static bool SetReplaceText(_In_ PCWSTR text);
inline void SetPersistState(bool persistState)
{
settings.persistState = persistState;
}
inline bool GetMRUEnabled() const
{
return settings.MRUEnabled;
}
inline void SetMRUEnabled(bool MRUEnabled)
{
settings.MRUEnabled = MRUEnabled;
}
inline long GetMaxMRUSize() const
{
return settings.maxMRUSize;
}
inline void SetMaxMRUSize(long maxMRUSize)
{
settings.maxMRUSize = maxMRUSize;
}
inline long GetFlags() const
{
return settings.flags;
}
inline void SetFlags(long flags)
{
settings.flags = flags;
Save();
}
inline const std::wstring& GetSearchText() const
{
return settings.searchText;
}
inline void SetSearchText(const std::wstring& text)
{
settings.searchText = text;
Save();
}
inline const std::wstring& GetReplaceText() const
{
return settings.replaceText;
}
inline void SetReplaceText(const std::wstring& text)
{
settings.replaceText = text;
Save();
}
void Save();
void Load();
private:
static bool GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue);
static bool SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value);
static bool SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value);
static DWORD GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue);
static bool SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value);
static bool GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf);
struct Settings
{
bool enabled{ true };
bool showIconOnMenu{ true };
bool extendedContextMenuOnly{ false }; // Disabled by default.
bool persistState{ true };
bool MRUEnabled{ true };
long maxMRUSize{ 10 };
long flags{ 0 };
std::wstring searchText{};
std::wstring replaceText{};
};
void Reload();
void MigrateFromRegistry();
void ParseJson();
Settings settings;
std::wstring jsonFilePath;
FILETIME lastLoadedTime;
};
CSettings& CSettingsInstance();
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk);
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk);

View File

@@ -19,3 +19,5 @@
#include <shlwapi.h>
#include <ProjectTelemetry.h>
#pragma comment(lib, "windowsapp")

View File

@@ -79,11 +79,11 @@ void Trace::SettingsChanged() noexcept
"PowerRename_SettingsChanged",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(CSettings::GetEnabled(), "IsEnabled"),
TraceLoggingBoolean(CSettings::GetShowIconOnMenu(), "ShowIconOnMenu"),
TraceLoggingBoolean(CSettings::GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
TraceLoggingBoolean(CSettings::GetPersistState(), "PersistState"),
TraceLoggingBoolean(CSettings::GetMRUEnabled(), "IsMRUEnabled"),
TraceLoggingUInt64(CSettings::GetMaxMRUSize(), "MaxMRUSize"),
TraceLoggingUInt64(CSettings::GetFlags(), "Flags"));
TraceLoggingBoolean(CSettingsInstance().GetEnabled(), "IsEnabled"),
TraceLoggingBoolean(CSettingsInstance().GetShowIconOnMenu(), "ShowIconOnMenu"),
TraceLoggingBoolean(CSettingsInstance().GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
TraceLoggingBoolean(CSettingsInstance().GetPersistState(), "PersistState"),
TraceLoggingBoolean(CSettingsInstance().GetMRUEnabled(), "IsMRUEnabled"),
TraceLoggingUInt64(CSettingsInstance().GetMaxMRUSize(), "MaxMRUSize"),
TraceLoggingUInt64(CSettingsInstance().GetFlags(), "Flags"));
}

View File

@@ -5,7 +5,6 @@
#include <commctrl.h>
#include <Shlobj.h>
#include <helpers.h>
#include <settings.h>
#include <windowsx.h>
#include <thread>
#include <trace.h>
@@ -80,6 +79,14 @@ inline int RECT_HEIGHT(RECT& r)
return r.bottom - r.top;
}
CPowerRenameUI::CPowerRenameUI() :
m_refCount(1)
{
CSettingsInstance().Load();
(void)OleInitialize(nullptr);
ModuleAddRef();
}
// IUnknown
IFACEMETHODIMP CPowerRenameUI::QueryInterface(__in REFIID riid, __deref_out void** ppv)
{
@@ -308,7 +315,7 @@ HRESULT CPowerRenameUI::_Initialize(_In_ IPowerRenameManager* psrm, _In_opt_ IUn
HRESULT CPowerRenameUI::_InitAutoComplete()
{
HRESULT hr = S_OK;
if (CSettings::GetMRUEnabled())
if (CSettingsInstance().GetMRUEnabled())
{
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spSearchAC));
if (SUCCEEDED(hr))
@@ -387,19 +394,13 @@ HRESULT CPowerRenameUI::_ReadSettings()
// Check if we should read flags from settings
// or the defaults from the manager.
DWORD flags = 0;
if (CSettings::GetPersistState())
if (CSettingsInstance().GetPersistState())
{
flags = CSettings::GetFlags();
flags = CSettingsInstance().GetFlags();
m_spsrm->put_flags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
buffer[0] = L'\0';
CSettings::GetSearchText(buffer, ARRAYSIZE(buffer));
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer);
buffer[0] = L'\0';
CSettings::GetReplaceText(buffer, ARRAYSIZE(buffer));
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer);
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, CSettingsInstance().GetSearchText().c_str());
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, CSettingsInstance().GetReplaceText().c_str());
}
else
{
@@ -414,18 +415,18 @@ HRESULT CPowerRenameUI::_ReadSettings()
HRESULT CPowerRenameUI::_WriteSettings()
{
// Check if we should store our settings
if (CSettings::GetPersistState())
if (CSettingsInstance().GetPersistState())
{
DWORD flags = 0;
m_spsrm->get_flags(&flags);
CSettings::SetFlags(flags);
CSettingsInstance().SetFlags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer, ARRAYSIZE(buffer));
CSettings::SetSearchText(buffer);
CSettingsInstance().SetSearchText(buffer);
if (CSettings::GetMRUEnabled() && m_spSearchACL)
if (CSettingsInstance().GetMRUEnabled() && m_spSearchACL)
{
CComPtr<IPowerRenameMRU> spSearchMRU;
if (SUCCEEDED(m_spSearchACL->QueryInterface(IID_PPV_ARGS(&spSearchMRU))))
@@ -436,9 +437,9 @@ HRESULT CPowerRenameUI::_WriteSettings()
buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer));
CSettings::SetReplaceText(buffer);
CSettingsInstance().SetReplaceText(buffer);
if (CSettings::GetMRUEnabled() && m_spReplaceACL)
if (CSettingsInstance().GetMRUEnabled() && m_spReplaceACL)
{
CComPtr<IPowerRenameMRU> spReplaceMRU;
if (SUCCEEDED(m_spReplaceACL->QueryInterface(IID_PPV_ARGS(&spReplaceMRU))))

Some files were not shown because too many files have changed in this diff Show More