diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index bfcdd94094..3c19d4949f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -10,7 +10,7 @@ body: - type: input attributes: label: Microsoft PowerToys version - placeholder: 0.63.0 + placeholder: 0.68.0 description: Hover over system tray icon or look at Settings validations: required: true @@ -60,9 +60,11 @@ body: - Installer - Keyboard Manager - Mouse Utilities + - Paste as Plain Text - PowerRename - PowerToys Run - Quick Accent + - Registry Preview - Screen ruler - Settings - Shortcut Guide diff --git a/.github/ISSUE_TEMPLATE/translation_issue.yml b/.github/ISSUE_TEMPLATE/translation_issue.yml index 787f9ebb69..de38f7b61d 100644 --- a/.github/ISSUE_TEMPLATE/translation_issue.yml +++ b/.github/ISSUE_TEMPLATE/translation_issue.yml @@ -12,7 +12,7 @@ body: - type: input attributes: label: Microsoft PowerToys version - placeholder: 0.63.0 + placeholder: 0.68.0 description: Hover over system tray icon or look at Settings validations: required: true @@ -34,9 +34,11 @@ body: - Installer - Keyboard Manager - Mouse Utilities + - Paste as Plain Text - PowerRename - PowerToys Run - Quick Accent + - Registry Preview - Screen ruler - Settings - Shortcut Guide diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 979f48ee4f..97520c020f 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -14,5 +14,6 @@ robmen skycommand snickler sinclairinat +TheJoeFin Vidia yifan diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index 1c42d9e4a9..3e04a7f21f 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -77,8 +77,10 @@ ^\.gitmodules$ ^\Q.github/workflows/spelling2.yml\E$ ^\Q.pipelines/ESRPSigning_core.json\E$ -^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$ +^\Qinstaller/PowerToysSetup/Settings.wxs\E$ ^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ +^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$ +^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/CorruptJson/Microsoft/PowerToys/settings.json\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.18.2/Microsoft/PowerToys/PowerRename/power-rename-ui-flags\E$ ^\Qsrc/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.19.2/Microsoft/PowerToys/PowerRename/power-rename-ui-flags\E$ @@ -97,6 +99,7 @@ ^src/modules/fancyzones/lib/FancyZonesWinHookEventIDs\.h$ ^src/modules/imageresizer/dll/ContextMenuHandler\.rgs$ ^src/modules/imageresizer/dll/ImageResizerExt\.rgs$ +^src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs$ ^src/modules/powerrename/testapp/PowerRenameTest\.vcxproj\.filters$ ^src/modules/previewpane/PreviewPaneUnitTests/HelperFiles/MarkdownWithHTMLImageTag\.txt$ ^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag.txt$ diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index dadf68c544..11cc127869 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1,15 +1,28 @@ aaaa +AAAAEF +AAB +AABAD +AACB +AACD +AAD +AADF +abap Abbrivation +ABE abgr abi ABlocked ABOUTBOX Abug +ACA accctrl Acceleratorkeys ACCEPTFILES ACCESSDENIED accessibilityinsights +ACDB +ACFC +ACFF Acl aclapi AColumn @@ -25,13 +38,22 @@ adml admx advapi advfirewall +AEAA +AEAD +AECC +AED +AEE +AEEB +AFAE +AFAEFC +AFDA +AFE AFeature +AFFE AFFINETRANSFORM AFX AGGREGATABLE AHybrid -Akrotiri -Aktobe ALarger alekhyareddy ALLAPPS @@ -41,11 +63,10 @@ ALPHATYPE Altdown alwaysontop amd -AMF AModifier AMPROPERTY AMPROPSETID -Andreanof +ANDSCANS anges angularsen Animatable @@ -53,6 +74,7 @@ ansicolor ANull AOC aocfnapldcnfbofgmbbllojgocaelgdd +AOT APARTMENTTHREADED APeriod apidl @@ -67,17 +89,15 @@ Applets Applicationcan applicationconfiguration applicationframehost -Applist applog appmanifest +APPNAME appref apps appwindow appwiz -appxpackage APSTUDIO AQS -Aqtobe ARCHITEW arcosh ARemapped @@ -96,11 +116,9 @@ arw asdf AShortcut ASingle -Asn ASSOCCHANGED ASYNCWINDOWPLACEMENT ASYNCWINDOWPOS -Atikokan atl atlbase atlcom @@ -108,7 +126,6 @@ atleast atlfile atlstr Attribs -Atyrau aumid Aut Authenticode @@ -119,20 +136,41 @@ AUTOMATIONPROPERTIES Autorun AUTOUPDATE AValid -Avanc awakeness awakeversion AYUV +azcli +azman +azor backtracer +BAF bak -Bashkortostan -Bayan +BBE bbwe +BCA +BCB +BCCE +BCCEA bck +BDB +BDBAD +BDCC +BDDF +BDFB +BEAA +BEB +BEEAADF +BEEC +BEFA Belarus BESTEFFORT betadele betsegaw +BFC +BFDE +BFEB +BFF +BFFA BGR bgra bhid @@ -147,8 +185,10 @@ BITMAPINFOHEADER bitmask BITSPIXEL bla +BLACKONWHITE Blockquotes blogs +Blt BLUEGRAY Bluetooth BLURBEHIND @@ -167,10 +207,10 @@ bpmf bpp bricelam BRIGHTGREEN -Brotli Browsable bsd bstr +bthprops bti btn BTNFACE @@ -182,38 +222,63 @@ buildtask buildtransitive Burkina Buryatia +BUTTONUP BVal BValue BYPOSITION bytearray -Caiguna +CABD CALG callbackptr +cameligo calpwstr Cangjie CANRENAME +CAPTUREBLT CAPTURECHANGED CARRAY CAtl +CBA +CBB +CBF +CCCCDE cch +CCHDEVICENAME +CCHFORMNAME CCom CContext +CDAC +CDBF +CDCE +CDD +CDE cdecl CDeclaration CDEF cdpx +CEAF +CEBAC +CEBD +CECB CElems CENTERALIGN +ceq +certlm +certmgr +CFAADB +CFBBF +CFEE +CFFEE +CFFF cguid changecursor Changemove chdir CHILDACTIVATE CHILDWINDOW -Choibalsan chrdavis Chrzan -cht +CHT Chukotka Chuuk cidl @@ -225,18 +290,16 @@ cla clangformat CLASSDC CLASSNOTAVAILABLE +clayton clickable clickonce CLIENTEDGE +clientid clientside CLIPCHILDREN -Clipperton CLIPSIBLINGS +Cloneable clrcall -clrcompression -clretwrc -clrgc -clrjit Cls CLSCTX clsid @@ -251,9 +314,8 @@ CMock CMONITORS cmpgt cmyk -Cng +cne cnt -Cocklebiddy coclass CODENAME codeofconduct @@ -266,17 +328,19 @@ colorformat colorhistory colorhistorylimit COLORKEY +COLORONCOLOR colorpicker COLORREF comctl COMDAT comdef comdlg +comexp cominterop commandline COMMANDTITLE commctrl -Comoros +compmgmt COMPOSITIONFULL comsupp comsuppw @@ -297,8 +361,6 @@ CONTROLL CONTROLPARENT Controlz copiedcolorrepresentation -coreclr -corewebview cortana cotaskmem COULDNOT @@ -309,9 +371,11 @@ cppruntime cppstd cppwinrt CProj +createdump CREATESCHEDULEDTASK CREATESTRUCT CREATEWINDOWFAILED +CRECT critsec Crossdevice CRSEL @@ -324,13 +388,12 @@ CSRW CStyle CSY CTest -Ctl CTRLALTDEL Ctrls Ctx CUI -Cunha currentculture +CURRENTDIR CURSORINFO cursorpos customaction @@ -342,14 +405,18 @@ cvtepu cvtsi cwd cxfksword +CXSCREEN CXSMICON CXVIRTUALSCREEN cyberrex +CYSCREEN CYSMICON CYVIRTUALSCREEN cziplib +DAA Dac dacl +DAF damienleroy DANGEROUSLYCOMMITMERELYTODISKCACHE Danmarkshavn @@ -357,27 +424,36 @@ DARKPURPLE DARKTEAL DARKYELLOW datareader -datatemplate Datavalue +dataversion DATAW davidegiacometti Dayof +DBAE +DBB +DBBDA +DBDE Dbg Dbghelp -dbgshim DBLCLKS DBLEPSILON +DCAB DCapture DCBA +DCBC +DCCB +DCEFCB +DCF DCOM dcommon dcomp dcompi -DCompiler DComposition -dcr -dcs +DCR +DCs Dct +DDCDD +DDCE DDEIf DDevice ddf @@ -388,6 +464,7 @@ debian debugbreak DECLAR declspec +DED Dedup DEFAULTBOOTSTRAPPERINSTALLFOLDER DEFAULTCOLOR @@ -401,6 +478,8 @@ DEFPUSHBUTTON deinitialization DELA DELAYCREATION +DELETEDKEYIMAGE +DELETESCANS deletethis Delimarsky dend @@ -409,23 +488,28 @@ Deondre depersist deprioritized depsfileslistspath -depsjsonpath deref DESKTOPABSOLUTEEDITING DESKTOPABSOLUTEPARSING desktopshorcutinstalled desktopwindowxamlsource -deu +DEU devblogs devdocs devenum +devmgmt +DEVMODEW DEVMON devpkey DEVSOURCE +DFAB +DFB +DFBEA DIIRFLAG dimm directshow DISABLEASACTIONKEY +diskmgmt DISPLAYCHANGE DISPLAYCONFIG displayname @@ -440,7 +524,6 @@ dllmain DNLEN Dns DONOTROUND -DONTRESOLVEDLLREFERENCES DONTVALIDATEPATH dotnet DPICHANGED @@ -455,6 +538,8 @@ dreamsofameaningfullife drf drivedetectionwarning dshow +DSTINVERT +DUMMYUNIONNAME dutil DVASPECT DVASPECTINFO @@ -487,13 +572,41 @@ dxgi dxgidebug dxgiformat dxguid +EAAFE +EABF +EAC +EADC +EAF +EBCF +EBD +EBE +ecl ecount EData +EDB +EDCCC +EDFAE Edid +edis EDITKEYBOARD editkeyboardwindow EDITSHORTCUTS editshortcutswindow +edshift +EEA +EEB +EEBBE +EEBD +EED +EEDA +EEEE +EEF +EEFA +EFB +EFC +EFDD +EFE +EFFEFC EFile eip ekus @@ -505,8 +618,7 @@ ENABLEDPOPUP endpointvolume endregion ENTERSIZEMOVE -enu -enumerationoptions +ENU EOAC epicgames epu @@ -514,19 +626,19 @@ ERASEBKGND EREOF EResize ERole +ERRORIMAGE ERRORLEVEL ERRORTITLE ESettings esize -esn esrp -Eswatini +estructuredtext etl etw -Eucla EUQ eurochange eventlog +eventvwr everytime evt EWXFORCE @@ -539,6 +651,7 @@ Exa exabyte examplehandler examplepowertoy +EXAND Excep EXCEPINFO EXCLUDEFROMCAPTURE @@ -559,16 +672,47 @@ EXTENDEDVERBS EXTRINSICPROPERTIES EXTRINSICPROPERTIESONLY eyetracker +FABC fabricbot -fancyzones +FAEDDA +FAF +FAFD +fancymouse +fancyzone FANCYZONESDRAWLAYOUTTEST FANCYZONESEDITOR Farbraum -Faroe FARPROC FASTPROPERTIESONLY +FBB +FBC +FBDE +FBF +FCAE +FCB +FCCFF +FCD +FCDB +FCDD +FCE +FDB +FDBF +FDC +FDCD +FDE +FDEF +FDF fdw +FECF +FEDF +FEEF feimage +FFB +FFBCF +FFBE +FFDDD +FFEB +FFEBEF fff fileapi FILEEXPLORER @@ -602,9 +746,9 @@ frankychen Froml FROMTOUCH FSCTL +fsmgmt FTYPE Functiondiscoverykeys -Futuna fwlink FZE gabime @@ -622,8 +766,6 @@ gcode gdi gdiplus GDISCALED -getancestor -getasynckeystate GETDESKWALLPAPER GETDLGCODE GETDPISCALEDSIZE @@ -633,13 +775,15 @@ GETSTATE GETTEXT GETTEXTLENGTH GHND -globalassemblycache -Globbing GMEM GNumber google +gpedit gpo +GPOCA +GPT gpu +graphql GSM gtm gui @@ -677,11 +821,11 @@ hcwhite hdc HDR hdrop +hdwwiz HEB Heiko Helpline helptext -Heure HGFE hglobal hhk @@ -717,13 +861,10 @@ HOMEPATH homljgmgpmcbpjbnjpfijnhipfkiclkd HOOKPROC Hostbackdropbrush -hostfxr -hostpolicy hotkeycontrol hotkeys hotlight hotspot -Hovd HPAINTBUFFER HPALETTE HRAWINPUT @@ -748,6 +889,7 @@ HVal HValue Hvci hwb +HWHEEL HWINEVENTHOOK hwnd HWNDFIRST @@ -772,7 +914,7 @@ IDecoder IDesktop IDirect idl -IDLIST +idlist IDOn IDR idx @@ -801,7 +943,6 @@ IMAGERESIZEREXT imageresizerinput imageresizersettings imagingdevices -Imc ime IMetadata imeutil @@ -817,7 +958,6 @@ Inlines inorder INPC inproc -inputdev INPUTHARDWARE INPUTKEYBOARD INPUTLANGCHANGED @@ -832,6 +972,7 @@ INSTALLFOLDERTOPREVIOUSINSTALLFOLDER INSTALLLOCATION INSTALLMESSAGE INSTALLPROPERTY +installscopeperuser INSTALLSTARTMENUSHORTCUT INSTALLSTATE Inste @@ -839,7 +980,6 @@ Intelli interactable Interlop INTRESOURCE -Intrinsics INVALIDARG invalidoperatioexception ipc @@ -847,12 +987,12 @@ ipcmanager IPlugin IPower IPREVIEW -ipreviewhandler ipreviewhandlervisualssetfont IProperty IPublic IQuery IReader +irprops isbi ISearch ISettings @@ -864,11 +1004,10 @@ iss ISurface ITask ith -ithumbnail +ITHUMBNAIL Ittoqqortoormiit IUI IUnknown -ivirtualdesktopmanager IWbem IWIC iwr @@ -880,12 +1019,13 @@ jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi jif jjw jobject +joefinapps jpe -jpn +JPN jpnime -JSONOf Jsons jsonval +julia junja jxr jyuwono @@ -904,27 +1044,22 @@ keydown keydropdowncontrol keyevent KEYEVENTF +KEYIMAGE keynum keyremaps Keytool keyup -Khakassia -Khanty -Khovd +Kfiles KILLFOCUS -Kitts +killrunner Knownfolders -Krai +kotlin KSPROPERTY -Kwango -Kwilu Kybd -Kyrgyzstan -Kyzylorda LAlt Lambson -LANGID langword +lastcodeanalysissucceeded Lastdevice Laute laviusmotileng @@ -941,13 +1076,16 @@ LCONTROL LCtrl Ldone ldx +LEFTDOWN LEFTSCROLLBAR +LEFTUP lego len LError Lessthan LEVELID LExit +lexon lhs lhwnd LIBID @@ -966,7 +1104,6 @@ LMEM LMENU lnk LOADFROMFILE -LOADLIBRARYASDATAFILE LOBYTE LOCALAPPDATA LOCALDISPLAY @@ -980,7 +1117,6 @@ logconsole logfile LOGFONT LOGFONTW -LOGMSG logon LOGPIXELSX LOn @@ -994,14 +1130,18 @@ LPBYTE LPCITEMIDLIST LPCMINVOKECOMMANDINFO LPCREATESTRUCT +LPCRECT LPCTSTR LPCWSTR lpdw lpfn +lpmi LPINPUT LPMINMAXINFO +LPMONITORINFO LPOSVERSIONINFOEXW lprc +LPPOINT LPRECT LPSAFEARRAY LPSTR @@ -1022,16 +1162,13 @@ lstrcmpi lstrlen LTRB LTRREADING -Luhansk luid +lusrmgr LVal LWA lwin LZero lzw -Maarten -Macquarie -Magadan Mainwindow majortype MAJORVERSION @@ -1039,10 +1176,7 @@ makecab MAKEINTRESOURCE MAKEINTRESOURCEW makepri -Mangere -Mangystau manifestdependency -Mansi MAPPEDTOSAMEKEY MAPTOSAMESHORTCUT MAPVK @@ -1050,15 +1184,14 @@ Markdig markdownpreviewhandler MARKDOWNPREVIEWHANDLERCPP Markovic -Marquesas martinchrzan martinmoene -Mato Maximizable MAXIMIZEBOX MAXSHORTCUTSIZE maxversiontested Mbits +MBR MBs MBUTTON MBUTTONDBLCLK @@ -1079,19 +1212,22 @@ Mega Melman MENUITEMINFO MENUITEMINFOW -menurc +MERGECOPY +MERGEPAINT Metadatas metafile mfapi mfc -mfcm mfidl mfobjects mfplat Mfsensorgroup mftransform +Mgmt mic microsoft +MIDDLEDOWN +MIDDLEUP Midl mii MIIM @@ -1106,11 +1242,12 @@ miniz minlevel MINORVERSION Miracast -Mishkeegogamang mjpg mkd mkdn mlcfg +mmc +mmcexe MMdd mmdeviceapi mmi @@ -1120,8 +1257,7 @@ mockapi MODECHANGE modernwpf MODESPRUNED -Moldova -Mongala +MONITORENUMPROC MONITORINFO MONITORINFOEX MONITORINFOEXW @@ -1145,11 +1281,9 @@ mrw msbuild msc msclr -mscordaccore -mscordbi mscorlib -mscorrc msdata +msdax msedge MSGFLT MSIFASTINSTALL @@ -1161,7 +1295,6 @@ MSIXCA MSLLHOOKSTRUCT Mso msp -msquic msrc msstore mst @@ -1171,15 +1304,12 @@ MTND Mul MULTIPLEUSE multizone -Mundrabilla mvvm myfile MYICON -MYTZ +mysql NAMECHANGE nameof -Navassa -navigatetostring NCACTIVATE ncc NCCALCSIZE @@ -1195,12 +1325,12 @@ NCMBUTTONUP NCMOUSELEAVE NCMOUSEMOVE NCol +ncpa NCPAINT NCRBUTTONDBLCLK NCRBUTTONDOWN NCRBUTTONUP NCRENDERING -Ndombe ndp NEEDDISPATCH neighborings @@ -1212,7 +1342,6 @@ netcpl netframework netsetup netsh -netstandard Neue newcolor newdev @@ -1225,11 +1354,13 @@ nielslaute NIF NLD nlog +nls NLSTEXT NOACTIVATE NOAGGREGATION NOASYNC NOCLOSEPROCESS +NOCOALESCE NOCOPYBITS nodeca nodiscard @@ -1238,13 +1369,13 @@ NOINHERITLAYOUT NOINTERFACE NOLINKINFO NOMINMAX +NOMIRRORBITMAP NOMOVE NONAME nonclient NONCONVERT NONELEVATED NONINFRINGEMENT -nonpackaged nonstd NOOPEN NOOWNERZORDER @@ -1258,7 +1389,6 @@ NOREPOSITION norestart NORMALDISPLAY NORMALUSER -Noronha NOSEARCH NOSENDCHANGING NOSIZE @@ -1270,6 +1400,8 @@ notmatch Noto NOTOPMOST NOTRACK +NOTSRCCOPY +NOTSRCERASE NOUPDATE NOZORDER NPH @@ -1277,15 +1409,13 @@ NResize nrw NTAPI ntdll -NTFS +ntfs NTSTATUS nugets nullonfailure numberbox NUMLOCK -NUMPAD -Nunavut -Nusa +numpad nwc Objbase OBJID @@ -1305,9 +1435,7 @@ oldpath oldtheme oleaut OLECHAR -oledb -oledbcommand -oledbconnection +OLEDB OLIVEGREEN onebranch onenote @@ -1326,6 +1454,7 @@ OPTIMIZEFORINVOKE ORAW ori ORPHANEDDIALOGTITLE +ORSCANS oss ostr OSVERSIONINFOEX @@ -1357,8 +1486,13 @@ PARENTRELATIVEPARSING PArgb parray PARTIALCONFIRMATIONDIALOGTITLE +pascaligo +pasteplain +PATCOPY pathcch Pathto +PATINVERT +PATPAINT PAUDIO pbc pbgra @@ -1380,8 +1514,10 @@ PDWORD pedrolamas pef PElems +Pels PERCEIVEDFLAG Percision +perfmon pesi petabyte peteblois @@ -1390,6 +1526,7 @@ pfn pfo pft pgp +pgsql pguid PHANDLE PHANDLER @@ -1404,9 +1541,7 @@ PINDIR pinfo pinvoke pipename -Pitcairn PKBDLLHOOKSTRUCT -Pkcs PKEY plib PLK @@ -1416,14 +1551,16 @@ plocm plugins pluginsmodel PMSIHANDLE -Pohnpei +Pnp Popups POPUPWINDOW posix +postiats poweraccent powerlauncher POWEROCR powerpreview +powerquery powerrename POWERRENAMECONTEXTMENU powerrenameinput @@ -1431,6 +1568,7 @@ POWERRENAMETEST powertoy POWERTOYNAME powertoyssetup +powertoysusersetup Powrprof ppenum ppidl @@ -1453,7 +1591,7 @@ Preinstalled prevhost Previer previewer -previewhandlerframeinfo +PREVIEWHANDLERFRAMEINFO previewpane previouscamera PREVIOUSINSTALLFOLDER @@ -1462,8 +1600,8 @@ prevpane PRGBA prgms pri -Primorsky PRINTCLIENT +printmanagement prm proactively PROCESSKEY @@ -1518,10 +1656,8 @@ QITAB QITABENT qps QUERYENDSESSION -queryfocus QUERYOPEN QUEUESYNC -Quic Quickime QUNS qwertyuiopasdfghjklzxcvbnm @@ -1549,8 +1685,8 @@ RECTDESTINATION RECTL rectp rects -recyclebin redirectedfrom +redis Redist redistributable reencode @@ -1558,16 +1694,21 @@ reencoded REFCLSID REFGUID REFIID +Refreshable REGCLS regedit regfile REGFILTER REGFILTERPINS REGISTERCLASSFAILED -registerhotkey +REGISTRYHEADER registrypath +registrypreview +REGISTRYPREVIEWEXT +registryroot regkey REGPINTYPES +regroot regsvr reinit REINSTALLMODE @@ -1577,6 +1718,7 @@ remappings REMAPSUCCESSFUL REMAPUNSUCCESSFUL Remotable +Removedir Removelnk renamable RENAMEONCOLLISION @@ -1588,9 +1730,9 @@ rescap resgen resheader resizers +RESIZETOFIT resmimetype RESOURCEID -resourcemanager RESTORETOMAXIMIZED restrictedcapabilities restrictederrorinfo @@ -1611,7 +1753,9 @@ rgs rhs ricardosantos RIDEV +RIGHTDOWN RIGHTSCROLLBAR +RIGHTUP riid RKey RLO @@ -1621,8 +1765,8 @@ roadmap robmensching Roboto rooler +rop roslyn -Rothera roundf ROUNDSMALL Rpc @@ -1630,11 +1774,11 @@ RRF rrr RSAT rshift +rsop Rsp Rstrtmgr RTB RTLREADING -RTSS ruleset runas rundll @@ -1642,11 +1786,11 @@ rungameid RUNLEVEL runsettings runtimeclass -runtimeconfig +runtimedepsjsonpath runtimeobject runtimepack runtimes -rus +RUS Rutkas RValue rvm @@ -1661,10 +1805,10 @@ SAFEARRAY safeprojectname SAMEKEYPREVIOUSLYMAPPED SAMESHORTCUTPREVIOUSLYMAPPED +Santossio SAVEFAILED scancode scanled -Schd schedtasks SCID Scip @@ -1678,10 +1822,9 @@ sddl SDKDDK sdns searchterm -secauthz +secpol Secur -securityoverview -segoe +Segoe Sekan SENDCHANGE sendinput @@ -1718,14 +1861,12 @@ shellex SHELLEXECUTEINFO SHELLEXECUTEINFOW shellscalingapi -shemptyrecyclebina SHFILEINFO SHGFI Shgno Shl shldisp shlobj -Shlw shlwapi shmem shobjidl @@ -1750,7 +1891,7 @@ SHOWMINNOACTIVE SHOWNA SHOWNOACTIVATE SHOWNORMAL -showwindow +SHOWWINDOW shtypes SICHINT sid @@ -1797,10 +1938,13 @@ spsi spsia spsrm spsv +SRCAND SRCCOPY +SRCERASE Srch +SRCINVERT +SRCPAINT sre -Srednekolymsk SResize srf SRGB @@ -1810,7 +1954,6 @@ srw srwlock sse ssf -Ssl STACKFRAME stackoverflow stackpanel @@ -1821,7 +1964,7 @@ STARTUPINFO STARTUPINFOEX STARTUPINFOW startupscreen -statflag +STATFLAG STATICEDGE STATSTG stdafx @@ -1859,9 +2002,7 @@ subkey SUBLANG subquery subresource -Sul Superbar -Suri sut SVE SVGIn @@ -1881,17 +2022,16 @@ SYSCOMMAND SYSDEADCHAR sysdm SYSICONINDEX -sysinfo SYSKEY syskeydown -syskeyup +SYSKEYUP SYSMENU SYSTEMAPPS systemroot SYSTEMTIME +systemverilog sysvol Tadele -Tajikistan talynone TApp TApplication @@ -1909,6 +2049,7 @@ taskkill tasklist taskschd tchar +tcl Tcollab tcs tcscpy @@ -1917,6 +2058,7 @@ tdbuild TDefault TDevice telem +telephon Templated templatenamespace Tenggara @@ -1943,6 +2085,7 @@ timeutil timezones titlebar Titlecase +TKey TLayout tlb tlbimp @@ -1955,7 +2098,6 @@ toolkitconverters Toolset toolwindow TOPDOWNDIB -tostring TOUCHEVENTF TOUCHINPUT touchpad @@ -1963,14 +2105,13 @@ Towindow tracelogging traies transicc -Transnistria TRAYMOUSEMESSAGE triaging TRK trl -Tshuapa +Tsd +TServer TStr -Tuva TValue TWF tymed @@ -1984,20 +2125,17 @@ TYPESHORTCUT UAC UAL uap -uapmanifestschema +uby udit Udk -Udmurtia Udp uefi UHash UIA -uiauto uid UIEx uipi UIs -Ulaanbaatar ULARGE ULONGLONG UMsg @@ -2027,7 +2165,6 @@ UOffset Updatelayout UPGRADINGPRODUCTCODE Uptool -Urville Usb USEDEFAULT USEFILEATTRIBUTES @@ -2042,13 +2179,13 @@ Utc utf UType uuidof -Uvs uwp uxtheme UYVY vabdq validmodulename Vanara +variantassignment VARTYPE vcamp vccorlib @@ -2083,7 +2220,7 @@ VIDEOINFOHEADER viewbox viewmodel vih -virtualkey +VIRTUALDESK visiblecolorformats Visibletrue VKey @@ -2113,7 +2250,7 @@ WANTPALM wbem wbemuuid WBounds -wca +Wca wcautil WCE wcex @@ -2132,6 +2269,7 @@ website wekyb Wevtapi wgpocpl +WHITEONBLACK whitespaces WIC wifi @@ -2139,7 +2277,6 @@ wikipedia wildcards winapi winappdriver -winauto wincodec Wincodecsdk wincolor @@ -2173,8 +2310,7 @@ winkey WINL winmd winmm -winmsg -winnt +WINNT winres winrt winsdk @@ -2184,7 +2320,7 @@ winternl WINTHRESHOLD winui winuiex -winuser +WINVER winxamlmanager wistd withinrafael @@ -2199,6 +2335,7 @@ wmain Wman wmi WMICIM +wmimgmt WMKEYDOWN WMKEYUP wmp @@ -2217,7 +2354,7 @@ workspaces wox wparam wpf -wpfgfx +wpfdepsjsonpath wpftmp wpr wprp @@ -2229,6 +2366,7 @@ writefile Wrk wrl WScan +wscui wsf wsh wsl @@ -2240,7 +2378,6 @@ wtoi WTS wtsapi WTSAT -wtypes Wubi wuceffectsi WVC @@ -2248,6 +2385,7 @@ WVk Wwan Wwanpp XAttribute +XAxis Xbox XBUTTON XBUTTONDBLCLK @@ -2255,7 +2393,9 @@ XBUTTONDOWN XBUTTONUP xcopy XDocument +XDOWN XElement +xfd XFile XIncrement XLoc @@ -2265,14 +2405,18 @@ XPixel XResource xsi XStr +XUP XVIRTUALSCREEN -Yamalia +YAxis +Yeet YIncrement yinle yinwang yinyue YOffset +ypescript YQuantized +YResolution YStr YUY yuyoyuppe @@ -2280,7 +2424,6 @@ YUYV YVIRTUALSCREEN YVU YVYU -Zabaykalsky ZEROINIT ZIndex zipfile @@ -2288,7 +2431,5 @@ zipfolder zonable zoneset Zoneszonabletester -Zonev -zopfli Zykova zzz diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index 774349778a..f17c5fb2aa 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -56,12 +56,15 @@ https?://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]* /gist\.github\.com/[^/]+/[0-9a-f]+ # msdn -\b(?:download\.visualstudio|docs|msdn)\.microsoft\.com/[-_a-zA-Z0-9()=./]* +\b(?:download\.visualstudio|docs|msdn|learn)\.microsoft\.com/[-_a-zA-Z0-9()=./]* # medium link\.medium\.com/[a-zA-Z0-9]+ \bmedium\.com/\@[^/]+/[-\w]+ +# experimentation urls +https?://default\.exp-tas\.com/[-_a-zA-Z0-9/]* + publicKeyToken=(['"]|)[0-9a-f]+\g{-1} \@sha256:[0-9a-f]{64}\b diff --git a/.github/workflows/package-submissions.yml b/.github/workflows/package-submissions.yml index 5ef20fec67..e99a5e7378 100644 --- a/.github/workflows/package-submissions.yml +++ b/.github/workflows/package-submissions.yml @@ -1,4 +1,4 @@ -name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository +name: Submit Microsoft.PowerToys package to Windows Package Manager Community Repository # based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml on: @@ -7,24 +7,25 @@ on: types: [published] jobs: - winget: name: Publish winget package runs-on: windows-latest steps: - name: Submit package to Windows Package Manager Community Repository run: | - + $wingetPackage = "Microsoft.PowerToys" $gitToken = "${{ secrets.PT_WINGET }}" - + $github = Invoke-RestMethod -uri "https://api.github.com/repos/Microsoft/PowerToys/releases" - + $targetRelease = $github | Where-Object -Property name -match 'Release'| Select -First 1 - $installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url - $installerUrlArm = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url + $installerUserX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*x64' | Select -ExpandProperty browser_download_url + $installerMachineX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url + $installerUserArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url + $installerMachineArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url $ver = $targetRelease.tag_name.Trim("v") - + # getting latest wingetcreate file iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - .\wingetcreate.exe update $wingetPackage -s -v $ver -u $installerUrl $installerUrlArm -t $gitToken + .\wingetcreate.exe update $wingetPackage -s -v $ver -u $installerUserX64Url $installerMachineX64Url $installerUserArmUrl $installerMachineArmUrl -t $gitToken diff --git a/.gitignore b/.gitignore index 14c1e39737..cbd74e790c 100644 --- a/.gitignore +++ b/.gitignore @@ -345,6 +345,3 @@ src/common/Telemetry/*.etl /src/modules/previewpane/SvgThumbnailProvider/$(SolutionDir)$(Platform)/$(Configuration)/modules/FileExplorerPreview/SvgThumbnailProvider.xml /src/modules/powerrename/ui/RCa24464 /src/modules/powerrename/ui/RCb24464 - -# Generated installer file for Monaco source files. -/installer/PowerToysSetup/MonacoSRC.wxs diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 5db90e70b6..01f6d011c6 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -23,6 +23,7 @@ "PowerToys.Settings.UI.Lib.dll", "PowerToys.GPOWrapper.dll", "PowerToys.GPOWrapperProjection.dll", + "PowerToys.AllExperiments.dll", "modules\\AlwaysOnTop\\PowerToys.AlwaysOnTop.exe", "modules\\AlwaysOnTop\\PowerToys.AlwaysOnTopModuleInterface.dll", @@ -35,6 +36,8 @@ "modules\\PowerOCR\\PowerToys.PowerOCR.dll", "modules\\PowerOCR\\PowerToys.PowerOCR.exe", + "modules\\PastePlain\\PowerToys.PastePlainModuleInterface.dll", + "modules\\Awake\\PowerToys.AwakeModuleInterface.dll", "modules\\Awake\\PowerToys.Awake.exe", "modules\\Awake\\PowerToys.Awake.dll", @@ -125,7 +128,6 @@ "modules\\launcher\\Plugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll", "modules\\launcher\\Plugins\\System\\Microsoft.PowerToys.Run.Plugin.System.dll", "modules\\launcher\\Plugins\\TimeDate\\Microsoft.PowerToys.Run.Plugin.TimeDate.dll", - "modules\\launcher\\Plugins\\TimeZone\\Microsoft.PowerToys.Run.Plugin.TimeZone.dll", "modules\\launcher\\Plugins\\WebSearch\\Community.PowerToys.Run.Plugin.WebSearch.dll", "modules\\launcher\\Plugins\\WindowsTerminal\\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll", @@ -136,7 +138,10 @@ "modules\\MouseUtils\\PowerToys.FindMyMouse.dll", "modules\\MouseUtils\\PowerToys.MouseHighlighter.dll", + "modules\\MouseUtils\\PowerToys.MouseJump.dll", "modules\\MouseUtils\\PowerToys.MousePointerCrosshairs.dll", + "modules\\MouseUtils\\MouseJumpUI\\PowerToys.MouseJumpUI.dll", + "modules\\MouseUtils\\MouseJumpUI\\PowerToys.MouseJumpUI.exe", "modules\\PowerAccent\\PowerAccent.Core.dll", "modules\\PowerAccent\\PowerToys.PowerAccent.dll", @@ -149,6 +154,10 @@ "modules\\PowerRename\\PowerToys.PowerRenameContextMenu.dll", "modules\\PowerRename\\PowerRenameContextMenuPackage.msix", + "modules\\RegistryPreview\\PowerToys.RegistryPreviewExt.dll", + "modules\\RegistryPreview\\PowerToys.RegistryPreview.dll", + "modules\\RegistryPreview\\PowerToys.RegistryPreview.exe", + "modules\\ShortcutGuide\\ShortcutGuide\\PowerToys.ShortcutGuide.exe", "modules\\ShortcutGuide\\ShortcutGuideModuleInterface\\PowerToys.ShortcutGuideModuleInterface.dll", @@ -209,6 +218,8 @@ "Mono.Cecil.Mdb.dll", "Mono.Cecil.Pdb.dll", "Mono.Cecil.Rocks.dll", + "Newtonsoft.Json.dll", + "Newtonsoft.Json.Bson.dll", "NLog.dll", "HtmlAgilityPack.dll", "Markdig.Signed.dll", @@ -240,11 +251,14 @@ "modules\\PowerAccent\\Vanara.PInvoke.Shell32.dll", "modules\\PowerAccent\\Vanara.PInvoke.ShlwApi.dll", "modules\\PowerAccent\\Vanara.PInvoke.User32.dll", + "modules\\RegistryPreview\\clrcompression.dll", + "modules\\RegistryPreview\\Microsoft.Graphics.Canvas.Interop.dll", "modules\\FileExplorerPreview\\Microsoft.Web.WebView2.Core.dll", "modules\\FileExplorerPreview\\Microsoft.Web.WebView2.WinForms.dll", "modules\\FileExplorerPreview\\Microsoft.Web.WebView2.Wpf.dll", "modules\\FileExplorerPreview\\WebView2Loader.dll", "modules\\Hosts\\Microsoft.Graphics.Canvas.Interop.dll", + "modules\\Hosts\\clrcompression.dll", "modules\\launcher\\e_sqlite3.dll", "modules\\launcher\\LazyCache.dll", "modules\\launcher\\SQLitePCLRaw.batteries_v2.dll", diff --git a/.pipelines/ESRPSigning_installer.json b/.pipelines/ESRPSigning_installer.json index dea4efc7f4..b20e2cdc82 100644 --- a/.pipelines/ESRPSigning_installer.json +++ b/.pipelines/ESRPSigning_installer.json @@ -5,8 +5,8 @@ { "MatchedPath": [ "PowerToysSetupCustomActions.dll", - "PowerToysSetup-*.exe", - "PowerToysSetup-*.msi" + "PowerToys*Setup-*.exe", + "PowerToys*Setup-*.msi" ], "SigningInfo": { "Operations": [ diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml index 26754babe8..0da849f2bb 100644 --- a/.pipelines/ci/templates/build-powertoys-steps.yml +++ b/.pipelines/ci/templates/build-powertoys-steps.yml @@ -69,7 +69,7 @@ steps: configPath: NuGet.config restoreSolution: PowerToys.sln restoreDirectory: '$(Build.SourcesDirectory)\packages' - + - task: VSBuild@1 displayName: 'Build PowerToys.sln' inputs: @@ -153,7 +153,7 @@ steps: filePath: '$(build.sourcesdirectory)\.pipelines\installWiX.ps1' - task: VSBuild@1 - displayName: 'Build PowerToys MSI' + displayName: 'Build PowerToys per-machine MSI' inputs: solution: '**\installer\PowerToysSetup.sln' vsVersion: 17.0 @@ -163,7 +163,7 @@ steps: maximumCpuCount: true - task: VSBuild@1 - displayName: 'Build PowerToys Bootstrapper' + displayName: 'Build PowerToys per-machine Bootstrapper' inputs: solution: '**\installer\PowerToysSetup.sln' vsVersion: 17.0 @@ -173,6 +173,43 @@ steps: clean: false maximumCpuCount: true +- task: PowerShell@2 + displayName: Clean installer dir before building per-user installer + inputs: + targetType: inline + script: git clean -xfd -e *exe -- .\installer\ + pwsh: true + +- task: NuGetCommand@2 + displayName: Restore NuGet packages for PowerToysSetup.sln + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: installer\PowerToysSetup.sln + restoreDirectory: '$(Build.SourcesDirectory)\installer\packages' + +- task: VSBuild@1 + displayName: 'Build PowerToys per-user MSI' + inputs: + solution: '**\installer\PowerToysSetup.sln' + vsVersion: 17.0 + platform: '$(BuildPlatform)' + configuration: '$(BuildConfiguration)' + msbuildArgs: /t:PowerToysInstaller ${{ parameters.additionalBuildArguments }} /p:PerUser=true + maximumCpuCount: true + +- task: VSBuild@1 + displayName: 'Build PowerToys per-user Bootstrapper' + inputs: + solution: '**\installer\PowerToysSetup.sln' + vsVersion: 17.0 + platform: '$(BuildPlatform)' + configuration: '$(BuildConfiguration)' + msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} /p:PerUser=true + clean: false + maximumCpuCount: true + # directly not doing WinAppDriver testing - task: VSTest@2 displayName: 'MS Tests' @@ -201,13 +238,13 @@ steps: **\Wox.Test.dll **\Microsoft.PowerToys.Run.Plugin.System.UnitTests.dll **\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.dll - **\Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.dll **\Microsoft.Plugin.WindowsTerminal.UnitTests.dll **\Microsoft.Plugin.WindowWalker.UnitTests.dll **\PreviewPaneUnitTests.dll **\UnitTests-SvgThumbnailProvider.dll **\UnitTests-SvgPreviewHandler.dll **\PowerToys.Hosts.Tests.dll + **\MouseJumpUI.UnitTests.dll !**\obj\** !**\ref\** @@ -227,10 +264,17 @@ steps: **\powerpreviewTest.dll **\UnitTests-FancyZones.dll !**\obj\** - + +- task: PowerShell@2 + displayName: Trigger dotnet welcome message so that it does not cause errors on other scripts + inputs: + targetType: 'inline' + script: | + dotnet list $(build.sourcesdirectory)\src\common\Common.UI\Common.UI.csproj package + - task: PowerShell@2 displayName: Verifying Notice.md and Nuget packages match inputs: filePath: '$(build.sourcesdirectory)\.pipelines\verifyNoticeMdAgainstNugetPackages.ps1' arguments: -path '$(build.sourcesdirectory)\' - pwsh: true \ No newline at end of file + pwsh: true diff --git a/.pipelines/installWiX.ps1 b/.pipelines/installWiX.ps1 index 18a81c93d3..ab770b8bf5 100644 --- a/.pipelines/installWiX.ps1 +++ b/.pipelines/installWiX.ps1 @@ -1,7 +1,7 @@ $ProgressPreference = 'SilentlyContinue' -$WixDownloadUrl = "https://github.com/JaneaSystems/wix3/releases/download/wix3-3.14.0.6526/wix314.exe" -$WixBinariesDownloadUrl = "https://github.com/JaneaSystems/wix3/releases/download/wix3-3.14.0.6526/wix314-binaries.zip" +$WixDownloadUrl = "https://wixtoolset.org/downloads/v3.14.0.6526/wix314.exe" +$WixBinariesDownloadUrl = "https://wixtoolset.org/downloads/v3.14.0.6526/wix314-binaries.zip" # Download WiX binaries and verify their hash sums Invoke-WebRequest -Uri $WixDownloadUrl -OutFile "$($ENV:Temp)\wix314.exe" diff --git a/.pipelines/installer-steps.yml b/.pipelines/installer-steps.yml new file mode 100644 index 0000000000..6ab7e01009 --- /dev/null +++ b/.pipelines/installer-steps.yml @@ -0,0 +1,141 @@ +parameters: + - name: versionNumber + type: string + default: "0.0.1" + - name: perUserArg + type: string + default: "false" + - name: buildSubDir + type: string + default: "MachineSetup" + - name: installerPrefix + type: string + default: "PowerToysSetup" + +steps: + - task: VSBuild@1 + displayName: Build PowerToysSetupCustomActions DLL # This dll needs to be build and signed before building the MSI. + inputs: + solution: "**/installer/PowerToysSetup.sln" + vsVersion: 17.0 + msbuildArgs: /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog /t:PowerToysSetupCustomActions /p:RunBuildEvents=true /p:PerUser=${{parameters.perUserArg}} + platform: $(BuildPlatform) + configuration: $(BuildConfiguration) + clean: true + maximumCpuCount: true + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: Sign PowerToysSetupCustomActions DLL + inputs: + ConnectedServiceName: "Terminal/Console/WinAppDriver Team Code Signing Connection" + FolderPath: 'installer/PowerToysSetupCustomActions/$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}' + signType: batchSigning + batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json' + ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' + + ## INSTALLER START + #### MSI BUILDING AND SIGNING + - task: VSBuild@1 + displayName: Build MSI + inputs: + solution: "**/installer/PowerToysSetup.sln" + vsVersion: 17.0 + msbuildArgs: /p:CIBuild=true /target:PowerToysInstaller /bl:$(Build.SourcesDirectory)\msbuild.binlog /p:RunBuildEvents=false /p:PerUser=${{parameters.perUserArg}} + platform: $(BuildPlatform) + configuration: $(BuildConfiguration) + clean: false # don't undo our hard work above by deleting the CustomActions dll + maximumCpuCount: true + + - task: CmdLine@2 + displayName: "Extracting MSI to verify contents" + inputs: + script: | + "C:\Program Files (x86)\WiX Toolset v3.14\bin\dark.exe" -x $(build.sourcesdirectory)\extractedMsi installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}\${{parameters.installerPrefix}}-${{ parameters.versionNumber }}-$(BuildPlatform).msi + dir $(build.sourcesdirectory)\extractedMsi + + # Did we sign all files + - task: PowerShell@1 + displayName: Verifying entire build is signed and version set + inputs: + scriptName: .pipelines/versionAndSignCheck.ps1 + arguments: -targetDir '$(build.sourcesdirectory)\extractedMsi\File' + + - task: PowerShell@1 + displayName: Verifying MSI Custom Actions DLL is signed + inputs: + scriptName: .pipelines/versionAndSignCheck.ps1 + arguments: -targetDir '$(build.sourcesdirectory)\extractedMsi\Binary' + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: Sign MSI + inputs: + ConnectedServiceName: "Terminal/Console/WinAppDriver Team Code Signing Connection" + FolderPath: 'installer/PowerToysSetup/$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}' + signType: batchSigning + batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json' + ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' + #### END MSI + #### BOOTSTRAP BUILDING AND SIGNING + + - task: VSBuild@1 + displayName: Build Bootstrapper + inputs: + solution: "**/installer/PowerToysSetup.sln" + vsVersion: 17.0 + msbuildArgs: /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog /t:PowerToysBootstrapper /p:PerUser=${{parameters.perUserArg}} + platform: $(BuildPlatform) + configuration: $(BuildConfiguration) + clean: false # don't undo our hard work above by deleting the MSI + maximumCpuCount: true + + - task: CmdLine@2 + displayName: "Insignia: Extract Engine from Bundle" + inputs: + script: '"C:\Program Files (x86)\WiX Toolset v3.14\bin\insignia.exe" -ib installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}\${{parameters.installerPrefix}}-${{ parameters.versionNumber }}-$(BuildPlatform).exe -o installer\engine.exe' + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: "ESRP CodeSigning (Engine)" + inputs: + ConnectedServiceName: "Terminal/Console/WinAppDriver Team Code Signing Connection" + FolderPath: "installer" + Pattern: engine.exe + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "KeyCode": "CP-230012", + "OperationCode": "SigntoolSign", + "Parameters": { + "OpusName": "Microsoft", + "OpusInfo": "http://www.microsoft.com", + "FileDigest": "/fd \"SHA256\"", + "PageHash": "/NPH", + "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + }, + "ToolName": "sign", + "ToolVersion": "1.0" + }, + { + "KeyCode": "CP-230012", + "OperationCode": "SigntoolVerify", + "Parameters": {}, + "ToolName": "sign", + "ToolVersion": "1.0" + } + ] + + - task: CmdLine@2 + displayName: "Insignia: Merge Engine into Bundle" + inputs: + script: '"C:\Program Files (x86)\WiX Toolset v3.14\bin\insignia.exe" -ab installer\engine.exe installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}\${{parameters.installerPrefix}}-${{ parameters.versionNumber }}-$(BuildPlatform).exe -o installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}\${{parameters.installerPrefix}}-${{ parameters.versionNumber }}-$(BuildPlatform).exe' + + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: Sign Bootstrapper + inputs: + ConnectedServiceName: "Terminal/Console/WinAppDriver Team Code Signing Connection" + FolderPath: 'installer/PowerToysSetup/$(BuildPlatform)\$(BuildConfiguration)\${{parameters.buildSubDir}}' + signType: batchSigning + batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json' + ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' + #### END BOOTSTRAP + ## END INSTALLER diff --git a/.pipelines/release-nuget.config b/.pipelines/release-nuget.config index e1d43f2b79..822b4f137c 100644 --- a/.pipelines/release-nuget.config +++ b/.pipelines/release-nuget.config @@ -4,6 +4,12 @@ + + + + + + diff --git a/.pipelines/release.yml b/.pipelines/release.yml index 01665c74c1..5da718fd27 100644 --- a/.pipelines/release.yml +++ b/.pipelines/release.yml @@ -23,6 +23,7 @@ parameters: variables: IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations SkipCppCodeAnalysis: 1 # Skip the code analysis to speed up release CI. It runs on PR CI, anyway + IsExperimentationLive: 1 # The build and installer use this to turn on experimentation name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) resources: @@ -174,17 +175,6 @@ jobs: clean: true maximumCpuCount: true - - task: VSBuild@1 - displayName: Build PowerToysSetupCustomActions - inputs: - solution: '**/installer/PowerToysSetup.sln' - vsVersion: 17.0 - msbuildArgs: /target:PowerToysSetupCustomActions /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - clean: true - maximumCpuCount: true - - task: VSBuild@1 displayName: Publish Settings for Packaging inputs: @@ -312,29 +302,10 @@ jobs: configuration: $(BuildConfiguration) maximumCpuCount: true - - task: VSBuild@1 - displayName: Build PowerToysSetupCustomActions DLL # This dll needs to be build and signed before building the MSI. - inputs: - solution: '**/installer/PowerToysSetup.sln' - vsVersion: 17.0 - msbuildArgs: /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog /t:PowerToysSetupCustomActions /p:RunBuildEvents=true - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - maximumCpuCount: true - #### MAIN SIGNING AREA # reference https://dev.azure.com/microsoft/Dart/_git/AppDriver?path=/ESRPSigning.json&version=GBarm64-netcore&_a=contents for winappdriver # https://dev.azure.com/microsoft/Dart/_git/AppDriver?path=/CIPolicy.xml&version=GBarm64-netcore&_a=contents - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: Sign PowerToysSetupCustomActions DLL - inputs: - ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection' - FolderPath: 'installer/PowerToysSetupCustomActions/$(BuildPlatform)\$(BuildConfiguration)' - signType: batchSigning - batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json' - ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 displayName: Sign Core PT inputs: @@ -355,114 +326,6 @@ jobs: #### END SIGNING ## END MAIN -## INSTALLER START -#### MSI BUILDING AND SIGNING - - task: VSBuild@1 - displayName: Build MSI - inputs: - solution: '**/installer/PowerToysSetup.sln' - vsVersion: 17.0 - msbuildArgs: /p:CIBuild=true /target:PowerToysInstaller /bl:$(Build.SourcesDirectory)\msbuild.binlog /p:RunBuildEvents=false - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - clean: false # don't undo our hard work above by deleting the CustomActions dll - maximumCpuCount: true - - - task: CmdLine@2 - displayName: 'Extracting MSI to verify contents' - inputs: - script: | - "C:\Program Files (x86)\WiX Toolset v3.14\bin\dark.exe" -x $(build.sourcesdirectory)\extractedMsi installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).msi - dir $(build.sourcesdirectory)\extractedMsi - -# Did we sign all files - - task: PowerShell@1 - displayName: Verifying entire build is signed and version set - inputs: - scriptName: .pipelines/versionAndSignCheck.ps1 - arguments: -targetDir '$(build.sourcesdirectory)\extractedMsi\File' - - - task: PowerShell@1 - displayName: Verifying MSI Custom Actions DLL is signed - inputs: - scriptName: .pipelines/versionAndSignCheck.ps1 - arguments: -targetDir '$(build.sourcesdirectory)\extractedMsi\Binary' - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: Sign MSI - inputs: - ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection' - FolderPath: 'installer/PowerToysSetup/$(BuildPlatform)\$(BuildConfiguration)' - signType: batchSigning - batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json' - ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' -#### END MSI - -#### BOOTSTRAP BUILDING AND SIGNING - - task: VSBuild@1 - displayName: Build Bootstrapper - inputs: - solution: '**/installer/PowerToysSetup.sln' - vsVersion: 17.0 - msbuildArgs: /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog /t:PowerToysBootstrapper - platform: $(BuildPlatform) - configuration: $(BuildConfiguration) - clean: false # don't undo our hard work above by deleting the MSI - maximumCpuCount: true - - - task: CmdLine@2 - displayName: 'Insignia: Extract Engine from Bundle' - inputs: - script: '"C:\Program Files (x86)\WiX Toolset v3.14\bin\insignia.exe" -ib installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).exe -o installer\engine.exe' - - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP CodeSigning (Engine)' - inputs: - ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection' - FolderPath: 'installer' - Pattern: engine.exe - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "KeyCode": "CP-230012", - "OperationCode": "SigntoolSign", - "Parameters": { - "OpusName": "Microsoft", - "OpusInfo": "http://www.microsoft.com", - "FileDigest": "/fd \"SHA256\"", - "PageHash": "/NPH", - "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - }, - "ToolName": "sign", - "ToolVersion": "1.0" - }, - { - "KeyCode": "CP-230012", - "OperationCode": "SigntoolVerify", - "Parameters": {}, - "ToolName": "sign", - "ToolVersion": "1.0" - } - ] - - - task: CmdLine@2 - displayName: 'Insignia: Merge Engine into Bundle' - inputs: - script: '"C:\Program Files (x86)\WiX Toolset v3.14\bin\insignia.exe" -ab installer\engine.exe installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).exe -o installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).exe' - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: Sign Bootstrapper - inputs: - ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection' - FolderPath: 'installer/PowerToysSetup/$(BuildPlatform)\$(BuildConfiguration)' - signType: batchSigning - batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json' - ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml' -#### END BOOTSTRAP -## END INSTALLER - - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: binlog' condition: failed() @@ -513,10 +376,39 @@ jobs: SourceFolder: $(Build.ArtifactStagingDirectory)/Symbols-$(BuildPlatform)/ RemoveSourceFolder: True + - template: installer-steps.yml + parameters: + versionNumber: ${{ parameters.versionNumber }} + perUserArg: "false" + buildSubDir: "MachineSetup" + installerPrefix: "PowerToysSetup" + + - task: PowerShell@2 + displayName: Clean installer dir before building per-user installer + inputs: + targetType: inline + script: git clean -xfd -e *exe -- .\installer\ + pwsh: true + + - task: NuGetCommand@2 + displayName: NuGet restore solutions dependencies + inputs: + command: restore + restoreSolution: 'installer/*.sln' + selectOrConfig: config + nugetConfigPath: .pipelines/release-nuget.config + + - template: installer-steps.yml + parameters: + versionNumber: ${{ parameters.versionNumber }} + perUserArg: "true" + buildSubDir: "UserSetup" + installerPrefix: "PowerToysUserSetup" + - task: CopyFiles@2 displayName: Copying setup file over inputs: - contents: '**/PowerToysSetup-*.exe' + contents: "**/PowerToys*Setup-*.exe" flattenFolders: True targetFolder: $(Build.ArtifactStagingDirectory) @@ -527,20 +419,29 @@ jobs: script: | $p = "$(System.ArtifactsDirectory)\"; $staging = "$(Build.ArtifactStagingDirectory)\" - $hash = ((get-item $p\*.exe | Get-FileHash).Hash); - $plat = "hash_$(BuildPlatform).txt"; - $combinedPath = $staging + $plat; + $userHash = ((get-item $p\PowerToysUserSetup*.exe | Get-FileHash).Hash); + $machineHash = ((get-item $p\PowerToysSetup*.exe | Get-FileHash).Hash); + $userPlat = "hash_user_$(BuildPlatform).txt"; + $machinePlat = "hash_machine_$(BuildPlatform).txt"; + $combinedUserPath = $staging + $userPlat; + $combinedMachinePath = $staging + $machinePlat; - echo $plat - echo $hash echo $p - echo $combinedPath + + echo $userPlat + echo $userHash + echo $combinedUserPath + + echo $machinePlat + echo $machineHash + echo $combinedMachinePath - $hash | out-file -filepath $combinedPath + $userHash | out-file -filepath $combinedUserPath + $machineHash | out-file -filepath $combinedMachinePath pwsh: true - + - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: PowerToySetup' + displayName: "Publish Artifact: PowerToySetup" inputs: PathtoPublish: $(System.ArtifactsDirectory) ArtifactName: setup-$(BuildPlatform) diff --git a/.pipelines/verifyArm64Configuration.ps1 b/.pipelines/verifyArm64Configuration.ps1 index 6a9cdd3986..56e06c8f65 100644 --- a/.pipelines/verifyArm64Configuration.ps1 +++ b/.pipelines/verifyArm64Configuration.ps1 @@ -22,7 +22,7 @@ catch { $solutionFile = [Microsoft.Build.Construction.SolutionFile]::Parse($solution); $arm64SlnConfigs = $solutionFile.SolutionConfigurations | Where-Object { - $_.PlatformName -eq "ARM64" + $_.PlatformName -ceq "ARM64" }; # Should have two configurations. Debug and Release. @@ -39,9 +39,9 @@ $projects = $solutionFile.ProjectsInOrder | Where-Object { # Enumerate through the projects and add any project with a mismatched platform and project configuration foreach ($project in $projects) { foreach ($slnConfig in $arm64SlnConfigs.FullName) { - if ($project.ProjectConfigurations.$slnConfig.FullName -ne $slnConfig) { - $errorTable[$project.ProjectName] += @("" - | Select-Object @{n = "Configuration"; e = { $project.ProjectConfigurations.$slnConfig.FullName } }, + if ($project.ProjectConfigurations.$slnConfig.FullName -cne $slnConfig) { + $errorTable[$project.ProjectName] += @(""` + | Select-Object @{n = "Configuration"; e = { $project.ProjectConfigurations.$slnConfig.FullName ?? "Missing platform" } }, @{n = "ExpectedConfiguration"; e = { $slnConfig } }) } } diff --git a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 index affff4c782..2ee58da745 100644 --- a/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 +++ b/.pipelines/verifyNoticeMdAgainstNugetPackages.ps1 @@ -38,6 +38,8 @@ $totalList = $projFiles | ForEach-Object -Parallel { if($nugetTemp -is [array] -and $nugetTemp.count -gt 3) { + # Need to debug this script? Uncomment this line. + # Write-Host $csproj "`r`n" $nugetTemp "`r`n" $temp = New-Object System.Collections.ArrayList $temp.AddRange($nugetTemp) $temp.RemoveRange(0, 3) diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 6008eca3a0..c39d7eac84 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -1,90 +1,82 @@ [CmdletBinding()] # todo: send in arch / conf, could send in actual path Param( - [Parameter(Mandatory=$True,Position=1)] - [AllowEmptyString()] - [string]$targetDir = $PSScriptRoot + '/../extractedMsi/File' + [Parameter(Mandatory = $True, Position = 1)] + [AllowEmptyString()] + [string]$targetDir = $PSScriptRoot + '/../extractedMsi/File' ) -$DirPath = $targetDir; #this file is in pipeline, we need root. -$items = Get-ChildItem -Path $DirPath -File -Include *.exe,*.dll,*.ttf,PTCustomActions -Recurse -Force -ErrorAction SilentlyContinue +$DirPath = $targetDir; #this file is in pipeline, we need root. +$items = Get-ChildItem -Path $DirPath -File -Include *.exe, *.dll, *.ttf, PTCustomActions -Recurse -Force -ErrorAction SilentlyContinue +$versionExceptions = @( + "Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll", + "Microsoft.Windows.ApplicationModel.Resources.Projection.dll", + "Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll", + "Microsoft.Windows.AppLifecycle.Projection.dll", + "Microsoft.Windows.System.Power.Projection.dll", + "Microsoft.WindowsAppRuntime.Bootstrap.Net.dll", + "Microsoft.Xaml.Interactions.dll", + "Microsoft.Xaml.Interactivity.dll", + "hyjiacan.py4n.dll", + "Microsoft.WindowsAppRuntime.Release.Net.dll", + "Microsoft.Windows.Widgets.Projection.dll") -join '|'; +$nullVersionExceptions = @( + "codicon.ttf", + "e_sqlite3.dll", + "vcamp140_app.dll", + "marshal.dll", + "Microsoft.UI.Composition.OSSupport.dll", + "Microsoft.UI.Xaml.Internal.dll", + "Microsoft.Windows.ApplicationModel.Resources.dll", + "Microsoft.WindowsAppRuntime.dll", + "Microsoft.WindowsAppRuntime.Bootstrap.dll", + "MRM.dll", + "PushNotificationsLongRunningTask.ProxyStub.dll", + "WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll", + "System.Diagnostics.EventLog.Messages.dll", + "Microsoft.Windows.Widgets.dll") -join '|'; $totalFailure = 0; Write-Host $DirPath; -if(-not (Test-Path $DirPath)) -{ +if (-not (Test-Path $DirPath)) { Write-Host "Folder does not exist!" } Write-Host "Total items: " $items.Count -if($items.Count -eq 0) -{ - # no items means something bad happened. We should fail ASAP - exit 1; +if ($items.Count -eq 0) { + # no items means something bad happened. We should fail ASAP + exit 1; } $items | ForEach-Object { - if($_.VersionInfo.FileVersion -eq "1.0.0.0" ) - { - # These items are exceptions that actually have the 1.0.0.0 version. - if ((-not $_.Name.EndsWith("Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Windows.ApplicationModel.Resources.Projection.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Windows.AppLifecycle.Projection.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Windows.System.Power.Projection.dll")) -and - (-not $_.Name.EndsWith("Microsoft.WindowsAppRuntime.Bootstrap.Net.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Xaml.Interactions.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Xaml.Interactivity.dll")) -and - (-not $_.Name.EndsWith("hyjiacan.py4n.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Windows.Widgets.Projection.dll")) -and - (-not $_.Name.EndsWith("Microsoft.WindowsAppRuntime.Release.Net.dll")) - ) - { - Write-Host "Version set to 1.0.0.0: " + $_.FullName - $totalFailure++; - } - } + if ($_.VersionInfo.FileVersion -eq "1.0.0.0" -and $_.Name -notmatch $versionExceptions) { + # These items are exceptions that actually have the 1.0.0.0 version. + Write-Host "Version set to 1.0.0.0: " + $_.FullName + $totalFailure++; + } + elseif ($_.VersionInfo.FileVersion -eq $null -and $_.Name -notmatch $nullVersionExceptions) { + # These items are exceptions that actually a version not set. + Write-Host "Version not set: " + $_.FullName + $totalFailure++; + } + elseif ($_.VersionInfo.ProductName -contains "PowerToys" -and $_.VersionInfo.LegalCopyright -notmatch "Copyright \(C\) $((Get-Date).Year)") { + # PowerToys assemblies that aren't updated to the current year in the copyright + Write-Host "Copyright year out of date: " + $_.FullName + $totalFailure++; + } + else { + $auth = Get-AuthenticodeSignature $_.FullName + if ($auth.SignerCertificate -eq $null) { + Write-Host "Not Signed: " + $_.FullName + $totalFailure++; + } + } } -$items | ForEach-Object { - if($_.VersionInfo.FileVersion -eq $null ) - { - # These items are exceptions that actually a version not set. - if ((-not $_.Name.EndsWith("codicon.ttf")) -and - (-not $_.Name.EndsWith("e_sqlite3.dll")) -and - (-not $_.Name.EndsWith("vcamp140_app.dll")) -and - (-not $_.Name.EndsWith("marshal.dll")) -and - (-not $_.Name.EndsWith("Microsoft.UI.Composition.OSSupport.dll")) -and - (-not $_.Name.EndsWith("Microsoft.UI.Xaml.Internal.dll")) -and - (-not $_.Name.EndsWith("Microsoft.Windows.ApplicationModel.Resources.dll")) -and - (-not $_.Name.EndsWith("Microsoft.WindowsAppRuntime.dll")) -and - (-not $_.Name.EndsWith("Microsoft.WindowsAppRuntime.Bootstrap.dll")) -and - (-not $_.Name.EndsWith("MRM.dll")) -and - (-not $_.Name.EndsWith("PushNotificationsLongRunningTask.ProxyStub.dll")) -and - (-not $_.Name.EndsWith("WindowsAppSdk.AppxDeploymentExtensions.Desktop.dll")) -and - (-not $_.Name.EndsWith("System.Diagnostics.EventLog.Messages.dll")) - ) - { - Write-Host "Version not set: " + $_.FullName - $totalFailure++; - } - } +if ($totalFailure -gt 0) { + exit 1 } -$items | ForEach-Object { - $auth = Get-AuthenticodeSignature $_.FullName - if($auth.SignerCertificate -eq $null) - { - Write-Host "Not Signed: " + $_.FullName - $totalFailure++; - } -} - -if($totalFailure -gt 0) -{ - exit 1 -} - -exit 0 +exit 0 \ No newline at end of file diff --git a/COMMUNITY.md b/COMMUNITY.md index 315498e287..8fce8b92f4 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -27,6 +27,9 @@ Heiko has helped triaging, discussing, and creating a substantial number of issu ### [@Jay-o-Way](https://github.com/Jay-o-Way) - Jay Jay has helped triaging, discussing, creating a substantial number of issues and PRs. +### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com) +Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor. + ### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/) Helping keep our spelling correct :) @@ -34,6 +37,9 @@ Helping keep our spelling correct :) Color Picker is from Martin. +### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com) +Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility. + ### [@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. @@ -93,6 +99,10 @@ PowerToys Awake is a tool to keep your computer awake. 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), color picker UX update and [icon design](https://github.com/microsoft/PowerToys/issues/1118). +### [@randyrants](https://github.com/randyrants) - [Randy Santossio](https://www.randyrants.com) + +Randy contributed Registry Preview and some very early conversations about keyboard remapping. + ### [@oldnewthing](https://github.com/oldnewthing) - Raymond Chen Find My Mouse is based on Raymond Chen's SuperSonar. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c22522bb4..01d1400264 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,10 @@ Upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) i Please comment on an issue to let us know you're interested in working on something before you start the work. Not only does this avoid multiple people unexpectedly working on the same thing at the same time but it enables us to make sure everyone is clear on what should be done to implement any new functionality. It's less work for everyone, in the long run, to establish this up front. +### Localization issues + +Please file localization issues, so our internal localization team can identify and fix them. However we currently don't accept community Pull Requests fixing localization issues. Localization is handled by the internal Microsoft team only. + ### To Spec or not to Spec A key point is for everyone to understand the approach that will be taken. We want to be sure if anyone does work, we will accept it in. Items that are larger in scope we'll want some type of spec to understand what is being planned and have a discussion. Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run. @@ -53,7 +57,7 @@ Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/ ### Naming of features and functionality -Naming should be descriptive and straight forward. We want names to be clear about functionality and usefulness moving forward. +Naming should be descriptive and straight forward. We want names to be clear about functionality and usefulness moving forward. ### How can I become a collaborator on the PowerToys team diff --git a/CppRuleSet.ruleset b/CppRuleSet.ruleset index 1a59aeeeb3..2718c2e3f1 100644 --- a/CppRuleSet.ruleset +++ b/CppRuleSet.ruleset @@ -79,12 +79,12 @@ - - + + - + diff --git a/Directory.Build.props b/Directory.Build.props index 01872028f9..3bd2b96984 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,9 +1,9 @@ - Copyright (C) 2022 Microsoft Corporation + Copyright (C) 2023 Microsoft Corporation Microsoft Corp. - Copyright (C) 2022 Microsoft Corporation + Copyright (C) 2023 Microsoft Corporation PowerToys Microsoft Corporation en-US @@ -29,14 +29,14 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000000..6a0575cbc9 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,65 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NOTICE.md b/NOTICE.md index 79ac437363..54aa0e9e95 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,4 +1,5 @@ # NOTICES AND INFORMATION + This software incorporates material from third parties. - Color Picker @@ -122,7 +123,7 @@ 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 +### Beta Tadele's Window Walker License **Source**: https://github.com/betsegaw/windowwalker @@ -167,11 +168,12 @@ SOFTWARE. ## PowerToy: Installer/Runner ### spdlog + **Source**: https://github.com/gabime/spdlog The MIT License (MIT) -Copyright (c) 2016 Gabi Melman. +Copyright (c) 2016 Gabi Melman. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -185,7 +187,7 @@ 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 +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 @@ -195,8 +197,8 @@ THE SOFTWARE. This software depends on the fmt lib (MIT License), and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst - ### expected-lite + **Source**: https://github.com/martinmoene/expected-lite Boost Software License - Version 1.0 - August 17th, 2003 @@ -224,6 +226,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ### zip + **Source**: https://github.com/kuba--/zip This is free and unencumbered software released into the public domain. @@ -254,6 +257,7 @@ For more information, please refer to ## Utility: Measure tool ### sse2neon + We adopted some functions from it. **Source**: https://github.com/DLTcollab/sse2neon @@ -277,7 +281,7 @@ SOFTWARE. ## NuGet Packages used by PowerToys -- CommunityToolkit.Labs.WinUI.SettingsControls 0.0.7 +- CommunityToolkit.Labs.WinUI.SettingsControls 0.0.18 - CommunityToolkit.Mvvm 8.0.0 - CommunityToolkit.WinUI.UI 7.1.2 - CommunityToolkit.WinUI.UI.Controls 7.1.2 @@ -289,16 +293,16 @@ SOFTWARE. - LazyCache 2.4.0 - Mages 2.0.1 - Markdig.Signed 0.27.0 -- Microsoft.CodeAnalysis.NetAnalyzers 7.0.0 +- Microsoft.CodeAnalysis.NetAnalyzers 7.0.1 - Microsoft.Data.Sqlite 7.0.0 - Microsoft.Extensions.Hosting 7.0.0 - Microsoft.NET.Test.Sdk 17.4.1 - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.1343.22 - Microsoft.Windows.CsWin32 0.2.46-beta -- Microsoft.Windows.CsWinRT 2.0.0 +- Microsoft.Windows.CsWinRT 2.0.1 - Microsoft.Windows.SDK.BuildTools 10.0.22621.755 -- Microsoft.WindowsAppSDK 1.2.221116.1 +- Microsoft.WindowsAppSDK 1.2.230313.1 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 @@ -325,4 +329,4 @@ SOFTWARE. - UnitsNet 4.145.0 - Vanara.PInvoke.Shell32 3.4.11 - Vanara.PInvoke.User32 3.4.11 -- WinUIEx 1.8.0 +- WinUIEx 2.1.0 diff --git a/PowerToys.sln b/PowerToys.sln index 14c89ce573..0a2f63de4c 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -139,7 +139,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\module {A2D583F0-B70C-4462-B1F0-8E81AFB7BA85} = {A2D583F0-B70C-4462-B1F0-8E81AFB7BA85} {BB23A474-5058-4F75-8FA3-5FE3DE53CDF4} = {BB23A474-5058-4F75-8FA3-5FE3DE53CDF4} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} - {F44934A8-36F3-49B0-9465-3831BE041CDE} = {F44934A8-36F3-49B0-9465-3831BE041CDE} {F8B870EB-D5F5-45BA-9CF7-A5C459818820} = {F8B870EB-D5F5-45BA-9CF7-A5C459818820} {FD8EB419-FF9C-4D88-BB6F-BF6CED37747B} = {FD8EB419-FF9C-4D88-BB6F-BF6CED37747B} {FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4} @@ -176,6 +175,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution src\.editorconfig = src\.editorconfig .vsconfig = .vsconfig Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props Solution.props = Solution.props EndProjectSection EndProject @@ -395,8 +395,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-StlThumbnailProvi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonacoPreviewHandler", "src\modules\previewpane\MonacoPreviewHandler\MonacoPreviewHandler.csproj", "{04B193D7-3E21-46B8-A958-89B63A8A69DE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeZone", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeZone\Microsoft.PowerToys.Run.Plugin.TimeZone.csproj", "{F44934A8-36F3-49B0-9465-3831BE041CDE}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeDate", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.csproj", "{5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests\Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj", "{FD464B4C-2F68-4D06-91E7-4208146C41F5}" @@ -409,8 +407,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUI", "src\module EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameContextMenu", "src\modules\powerrename\PowerRenameContextMenu\PowerRenameContextMenu.vcxproj", "{1DBBB112-4BB1-444B-8EBB-E66555C76BA6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests\Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj", "{C5D46169-5334-48C3-8C28-644C72832E54}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.OneNote", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.OneNote\Microsoft.PowerToys.Run.Plugin.OneNote.csproj", "{5A1DB2F0-0715-4B3B-98E6-79BC41540045}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerContextMenu", "src\modules\imageresizer\ImageResizerContextMenu\ImageResizerContextMenu.vcxproj", "{93B72A06-C8BD-484F-A6F7-C9F280B150BF}" @@ -498,6 +494,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StlThumbnailProviderCpp", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SvgThumbnailProviderCpp", "src\modules\previewpane\SvgThumbnailProviderCpp\SvgThumbnailProviderCpp.vcxproj", "{2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouseJump", "src\modules\MouseUtils\MouseJump\MouseJump.vcxproj", "{8A08D663-4995-40E3-B42C-3F910625F284}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI", "src\modules\MouseUtils\MouseJumpUI\MouseJumpUI.csproj", "{D962A009-834F-4EEC-AABB-430DF8F98E39}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI.UnitTests", "src\modules\MouseUtils\MouseJumpUI.UnitTests\MouseJumpUI.UnitTests.csproj", "{D9C5DE64-6849-4278-91AD-9660AECF2876}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pasteplain", "pasteplain", "{9873BA05-4C41-4819-9283-CF45D795431B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PastePlainModuleInterface", "src\modules\pasteplain\PastePlainModuleInterface\PastePlainModuleInterface.vcxproj", "{FC373B24-3293-453C-AAF5-CF2909DCEE6A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RegistryPreviewUI", "src\modules\registrypreview\RegistryPreviewUI\RegistryPreviewUI.csproj", "{FD86C06A-FB54-4D5E-9831-1CDADF60D45F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RegistryPreviewExt", "src\modules\registrypreview\RegistryPreviewExt\RegistryPreviewExt.vcxproj", "{697C6AF9-0A48-49A9-866C-67DA12384015}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RegistryPreview", "RegistryPreview", "{929C1324-22E8-4412-A9A8-80E85F3985A5}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilePreviewCommon", "src\common\FilePreviewCommon\FilePreviewCommon.csproj", "{9EBAA524-0EDA-470B-95D4-39383285CBB2}" EndProject Global @@ -1587,17 +1601,6 @@ Global {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x64.Build.0 = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|x64 {04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.Build.0 = Release|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|ARM64.Build.0 = Debug|ARM64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x64.ActiveCfg = Debug|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x64.Build.0 = Debug|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Debug|x86.ActiveCfg = Debug|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|ARM64.ActiveCfg = Release|ARM64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|ARM64.Build.0 = Release|ARM64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x64.ActiveCfg = Release|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x64.Build.0 = Release|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x86.ActiveCfg = Release|x64 - {F44934A8-36F3-49B0-9465-3831BE041CDE}.Release|x86.Build.0 = Release|x64 {5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Debug|ARM64.Build.0 = Debug|ARM64 {5BDBD6C9-A31F-4CEB-871A-5E9E709197EF}.Debug|x64.ActiveCfg = Debug|x64 @@ -1669,18 +1672,6 @@ Global {1DBBB112-4BB1-444B-8EBB-E66555C76BA6}.Release|x64.Build.0 = Release|x64 {1DBBB112-4BB1-444B-8EBB-E66555C76BA6}.Release|x86.ActiveCfg = Release|x64 {1DBBB112-4BB1-444B-8EBB-E66555C76BA6}.Release|x86.Build.0 = Release|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Debug|ARM64.Build.0 = Debug|ARM64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Debug|x64.ActiveCfg = Debug|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Debug|x64.Build.0 = Debug|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Debug|x86.ActiveCfg = Debug|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Debug|x86.Build.0 = Debug|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Release|ARM64.ActiveCfg = Release|ARM64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Release|ARM64.Build.0 = Release|ARM64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Release|x64.ActiveCfg = Release|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Release|x64.Build.0 = Release|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Release|x86.ActiveCfg = Release|x64 - {C5D46169-5334-48C3-8C28-644C72832E54}.Release|x86.Build.0 = Release|x64 {5A1DB2F0-0715-4B3B-98E6-79BC41540045}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5A1DB2F0-0715-4B3B-98E6-79BC41540045}.Debug|ARM64.Build.0 = Debug|ARM64 {5A1DB2F0-0715-4B3B-98E6-79BC41540045}.Debug|x64.ActiveCfg = Debug|x64 @@ -2101,6 +2092,90 @@ Global {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x64.Build.0 = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.ActiveCfg = Release|x64 {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA}.Release|x86.Build.0 = Release|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Debug|ARM64.Build.0 = Debug|ARM64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Debug|x64.ActiveCfg = Debug|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Debug|x64.Build.0 = Debug|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Debug|x86.ActiveCfg = Debug|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Debug|x86.Build.0 = Debug|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Release|ARM64.ActiveCfg = Release|ARM64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Release|ARM64.Build.0 = Release|ARM64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.ActiveCfg = Release|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.Build.0 = Release|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.ActiveCfg = Release|x64 + {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.Build.0 = Release|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.Build.0 = Debug|ARM64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.ActiveCfg = Debug|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.Build.0 = Debug|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x86.ActiveCfg = Debug|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x86.Build.0 = Debug|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|ARM64.ActiveCfg = Release|ARM64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|ARM64.Build.0 = Release|ARM64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x64.ActiveCfg = Release|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x64.Build.0 = Release|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x86.ActiveCfg = Release|x64 + {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x86.Build.0 = Release|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|ARM64.Build.0 = Debug|ARM64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x64.ActiveCfg = Debug|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x64.Build.0 = Debug|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x86.ActiveCfg = Debug|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x86.Build.0 = Debug|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|ARM64.ActiveCfg = Release|ARM64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|ARM64.Build.0 = Release|ARM64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x64.ActiveCfg = Release|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x64.Build.0 = Release|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x86.ActiveCfg = Release|x64 + {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x86.Build.0 = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.Build.0 = Debug|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x64.ActiveCfg = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x64.Build.0 = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x86.ActiveCfg = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x86.Build.0 = Debug|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|ARM64.ActiveCfg = Release|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|ARM64.Build.0 = Release|ARM64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x64.ActiveCfg = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x64.Build.0 = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x86.ActiveCfg = Release|x64 + {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Release|x86.Build.0 = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|ARM64.Build.0 = Debug|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.ActiveCfg = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x64.Build.0 = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x86.ActiveCfg = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Debug|x86.Build.0 = Debug|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|ARM64.ActiveCfg = Release|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|ARM64.Build.0 = Release|ARM64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.ActiveCfg = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x64.Build.0 = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.ActiveCfg = Release|x64 + {9CE59ED5-7087-4353-88EB-788038A73CEC}.Release|x86.Build.0 = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|ARM64.Build.0 = Debug|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x64.ActiveCfg = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x64.Build.0 = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x86.ActiveCfg = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Debug|x86.Build.0 = Debug|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|ARM64.ActiveCfg = Release|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|ARM64.Build.0 = Release|ARM64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x64.ActiveCfg = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x64.Build.0 = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x86.ActiveCfg = Release|x64 + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F}.Release|x86.Build.0 = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|ARM64.Build.0 = Debug|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x64.ActiveCfg = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x64.Build.0 = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x86.ActiveCfg = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Debug|x86.Build.0 = Debug|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|ARM64.ActiveCfg = Release|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|ARM64.Build.0 = Release|ARM64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x64.ActiveCfg = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x64.Build.0 = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x86.ActiveCfg = Release|x64 + {697C6AF9-0A48-49A9-866C-67DA12384015}.Release|x86.Build.0 = Release|x64 {9EBAA524-0EDA-470B-95D4-39383285CBB2}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9EBAA524-0EDA-470B-95D4-39383285CBB2}.Debug|ARM64.Build.0 = Debug|ARM64 {9EBAA524-0EDA-470B-95D4-39383285CBB2}.Debug|x64.ActiveCfg = Debug|x64 @@ -2236,14 +2311,12 @@ Global {F7C8C0F1-5431-4347-89D0-8E5354F93CF2} = {2F305555-C296-497E-AC20-5FA1B237996A} {F1F6B6B6-9F18-4A17-8B5C-97DF552C53DC} = {2F305555-C296-497E-AC20-5FA1B237996A} {04B193D7-3E21-46B8-A958-89B63A8A69DE} = {2F305555-C296-497E-AC20-5FA1B237996A} - {F44934A8-36F3-49B0-9465-3831BE041CDE} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {5BDBD6C9-A31F-4CEB-871A-5E9E709197EF} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {FD464B4C-2F68-4D06-91E7-4208146C41F5} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {020A7474-3601-4160-A159-D7B70B77B15F} = {C3081D9A-1586-441A-B5F4-ED815B3719C1} {27718999-C175-450A-861C-89F911E16A88} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} {1DBBB112-4BB1-444B-8EBB-E66555C76BA6} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} - {C5D46169-5334-48C3-8C28-644C72832E54} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {5A1DB2F0-0715-4B3B-98E6-79BC41540045} = {4AFC9975-2456-4C70-94A4-84073C1CED93} {93B72A06-C8BD-484F-A6F7-C9F280B150BF} = {6C7F47CC-2151-44A3-A546-41C70025132C} {18B3DB45-4FFE-4D01-97D6-5223FEEE1853} = {6C7F47CC-2151-44A3-A546-41C70025132C} @@ -2285,6 +2358,15 @@ Global {CA5518ED-0458-4B09-8F53-4122B9888655} = {2F305555-C296-497E-AC20-5FA1B237996A} {D6DCC3AE-18C0-488A-B978-BAA9E3CFF09D} = {2F305555-C296-497E-AC20-5FA1B237996A} {2BBC9E33-21EC-401C-84DA-BB6590A9B2AA} = {2F305555-C296-497E-AC20-5FA1B237996A} + {8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A} + {D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A} + {D9C5DE64-6849-4278-91AD-9660AECF2876} = {322566EF-20DC-43A6-B9F8-616AF942579A} + {9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} + {FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B} + {9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482} + {FD86C06A-FB54-4D5E-9831-1CDADF60D45F} = {929C1324-22E8-4412-A9A8-80E85F3985A5} + {697C6AF9-0A48-49A9-866C-67DA12384015} = {929C1324-22E8-4412-A9A8-80E85F3985A5} + {929C1324-22E8-4412-A9A8-80E85F3985A5} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {9EBAA524-0EDA-470B-95D4-39383285CBB2} = {1AFB6476-670D-4E80-A464-657E01DFF482} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/README.md b/README.md index 737071f805..4ecd7e1d72 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,10 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline | [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | -| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | -| [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | -| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | +| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | +| [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | +| [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | +| [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | ## Installing and running Microsoft PowerToys @@ -34,27 +35,36 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline ### Via GitHub with EXE [Recommended] -Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate the PowerToys installer that matches your machine's architecture. For most, it is `x64`. +Go to [Microsoft PowerToys GitHub releases page][github-release-link], click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. - - **For x64 processors (most common):** [PowerToysSetup-0.66.0-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.66.0/PowerToysSetup-0.66.0-x64.exe) - - **For ARM64 processors:** [PowerToysSetup-0.66.0-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.66.0/PowerToysSetup-0.66.0-arm64.exe) + - **For x64 processors (most common) per-user installer:** [PowerToysUserSetup-0.69.1-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysUserSetup-0.69.1-x64.exe) + - **For x64 processors per-machine installer:** [PowerToysSetup-0.69.1-x64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysSetup-0.69.1-x64.exe) + - **For ARM64 processors per-user installer:** [PowerToysUserSetup-0.69.1-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysUserSetup-0.69.1-arm64.exe) + - **For ARM64 processors per-machine installer:** [PowerToysSetup-0.69.1-arm64.exe](https://github.com/microsoft/PowerToys/releases/download/v0.69.1/PowerToysSetup-0.69.1-arm64.exe) This is our preferred method. ### Via Microsoft Store -Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/) which will be available for both Windows 11 and Windows 10. +Install from the [Microsoft Store's PowerToys page][microsoft-store-link]. You must be using the [new Microsoft Store](https://blogs.windows.com/windowsExperience/2021/06/24/building-a-new-open-microsoft-store-on-windows-11/) which is available for both Windows 11 and Windows 10. -### Via WinGet (Preview) -Download PowerToys from [WinGet][winget-link]. To install PowerToys, run the following command from the command line / PowerShell: +### Via WinGet +Download PowerToys from [WinGet][winget-link]. Updating PowerToys via winget will respect current PowerToys installation scope. To install PowerToys, run the following command from the command line / PowerShell: +#### User scope installer [default] ```powershell winget install Microsoft.PowerToys -s winget ``` +#### Machine-wide scope installer + +```powershell +winget install --scope machine Microsoft.PowerToys -s winget +``` + ### Other install methods -There are [community driven install methods](./doc/unofficialInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, this will have the install instructions. +There are [community driven install methods](./doc/unofficialInstallMethods.md) such as Chocolatey and Scoop. If these are your preferred install solutions, you can find the install instructions there. ## Contributing @@ -72,32 +82,35 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/ Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on. -### 0.66 - December 2022 Update +### 0.69 - March 2023 Update -In this release, we focused on stability and improvements. +In this release, we focused on releasing new features, stability and improvements. Early notice for v0.70, we will be releasing it later in May 2023. **Highlights** -- PowerToy utilities now ship with self-contained .NET 7, meaning it's not necessary to install .NET as part of the installer and it's easier to keep up to date. -- It's possible to pick which of the installed OCR languages is used by Text Extractor by selecting it in the right-click context menu. -- Added a setting to sort the order of the accented characters by usage frequency in Quick Accent. +- New utility: Registry Preview is a utility to visualize and edit Windows Registry files. Thanks [@randyrants](https://github.com/randyrants)! +- Support per-user scope installation. +- Awake: Quality-of-life improvements and introduced keeping system awake until expiration time and date. Thanks [@dend](https://github.com/dend)! +- PowerToys Run: Fix crashing issue caused by thumbnail image loading. ### General -- Reduced resource consumption caused by logging. A thread for each logger was being created even for disabled utilities. -- The .NET 7 dependency is now shipped self-contained within the utilities, using deep links to reduce storage space usage. +- New utility: Registry Preview. Thanks [@randyrants](https://github.com/randyrants)! +- Fix issue causing folders to not be removed on uninstall. +- Support per-user scope installation. + - Companies can control this using the new GPO. + +### Awake + +- Quality-of-life improvements and introduced keeping system awake until expiration time and date. Thanks [@dend](https://github.com/dend)! ### Color Picker -- Fixed an issue where the custom color formats were not working when picking colors without using the editor. -- Fixed a crash when using duplicated names for color formats. -- Added two decimal formats, to distinguish between RGB and BGR. -- Fixed color name localization, which was not working correctly on 0.65. +- Fix issue sampling timing and grid issue causing Color Picker to sample the color of its own grid. Thanks [@IHorvalds](https://github.com/IHorvalds)! ### FancyZones -- Fixed an editor crash caused by deleting a zone while trying to move it. -- Reduce the time it takes the tooltip for layout shortcut setting to appear in the editor. +- Fix window cycling on multiple monitors issue. ### File explorer add-ons @@ -105,73 +118,80 @@ In this release, we focused on stability and improvements. ### File Locksmith -- Fixed an issue causing File Locksmith to hang when looking for open handles in some machines. +- Add context menu icon. Thanks [@htcfreek](https://github.com/htcfreek)! -### Hosts File Editor +### Mouse Utils -- Added a warning when duplicated entries are detected. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! +- Mouse Jump - Simulate mouse input event on mouse jump in addition to cursor move. +- Mouse Jump - Improve performance of screenshot generation. Thanks [@mikeclayton](https://github.com/mikeclayton)! + +### Paste as Plain Text + +- Support Ctrl+V as activation shortcut. (This was a hotfix for 0.67) +- Repress modifier keys after plain paste. (This was a hotfix for 0.67) Thanks [@UnderKoen](https://github.com/UnderKoen)! +- Set default shortcut to Ctrl+Win+Alt+V. (This was a hotfix for 0.67) +- Update icons. Thanks [@niels9001](https://github.com/niels9001)! + +### PowerRename + +- Show PowerRename in directory background context menu. +- Fix the crash on clicking Select/UnselectAll checkbox while showing only files to be renamed. +- Improve performance on populating Renamed items when many items are being renamed. ### PowerToys Run -- Support drag and dropping for file results. Thanks [@daniel-richter](https://github.com/daniel-richter)! +- Add setting to disable thumbnails generation for files. (This was a hotfix for 0.67) +- Calculator plugin - handle implied multiplication expressions. Thanks [@jjavierdguezas](https://github.com/jjavierdguezas)! +- Fix Calculator plugin unit tests to respect decimal separator locale. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! +- Fix crashing caused by thumbnail image loading. +- Date & Time plugin - Add filename-compatible date & time format. Thanks [@Picazsoo](https://github.com/Picazsoo)! +- Improved the error message shown on plugin loading error. Thanks [@htcfreek](https://github.com/htcfreek)! ### Quick Accent -- Added support for dark theme. Thanks [@niels9001](https://github.com/niels9001)! -- Increased default input delay to improve out of the box experience. -- Fixed a bug causing the first character to not be selected when opening the overlay. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -- Fixed the positioning of the overlay when showing near the horizontal edges of the screen. -- Added additional Pinyin characters. Thanks [@char-46](https://github.com/char-46)! -- Added Macedonian characters. Thanks [@ad-mca-mk](https://github.com/ad-mca-mk)! -- Added a setting to sort characters by usage frequency. -- Added a setting to always start selection in the first character, even when using the arrow keys as the activation method. +- Fix existing and add missing Hebrew and Pinyin characters. Thanks [@stevenlele](https://github.com/stevenlele)! -### Settings +### Registry Preview -- Fixed an error that hid the option to keep the display on when using the "Indefinitely Awake" mode. -- Fixed an accessibility issue causing the navigation bar to not work with narrator in scan mode. -- Fixed an accessibility issue where the name for the shortcut control was not being read correctly. -- Tweaked the Color Picker custom color format UI. Thanks [@niels9001](https://github.com/niels9001)! -- Improved the shortcut control visibility and accessibility. Thanks [@niels9001](https://github.com/niels9001)! -- Fixed an issue causing the Settings to not be saved correctly on scenarios where the admin user would be different then the user running PowerToys. -- Added a setting to pick which language should be used by default when using Text Extractor. - -### Text Extractor - -- Improve behavior for CJK languages by not adding spaces for some characters that don't need them. Thanks [@AO2233](https://github.com/AO2233)! -- OCR language can now be picked in the right-click context menu. +- Added a new utility: Registry Preview. +- Thanks [@htcfreek](https://github.com/htcfreek)! for the help shipping this utility! +- Thanks [@niels9001](https://github.com/niels9001) for the help on the UI! ### Video Conference Mute -- Reduced resource consumption by not starting the File Watchers when the utility is disabled. +- Add toolbar DPI scaling support. +- Fix selecting overlay image when Settings app is running elevated. +- Add push-to-talk (and push-to-reverse) feature. Thanks [@pajawojciech](https://github.com/pajawojciech)! + +### Settings + +- Fix Experiment bitmap icon rendering on theme change and bump CommunityToolkit.Labs.WinUI.SettingsControls package version. Thanks [@niels9001](https://github.com/niels9001)! +- Video Conference Mute page improvements. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! +- Add warning that PowerToys Run might get no focus if "Use centralized keyboard hook" settings is enabled. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! +- Fix ShortcutControl issues related to keyboard input focus, theme change and missing error badge when invalid key is pressed. Thanks [@htcfreek](https://github.com/htcfreek)! +- Add warning when Ctrl+V and Ctrl+Shift+V is used as an activation shortcut for Paste as Plain Text. Thanks [@htcfreek](https://github.com/htcfreek)! ### Documentation -- Updated the development setup documentation. -- Improved the Markdown documentation lists numbering in many docs. Thanks [@sanidhyas3s](https://github.com/sanidhyas3s)! +- Update CONTRIBUTING.md with information about localization issues. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! +- Remove localization from URLs. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! +- Add dev docs for tools. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! ### Development -- Turned on C++ code analysis and incrementally fixing warnings. -- C++ code analysis no longer runs on release CI to speed up building release candidates. It still runs on GitHub CI and when building locally to maintain code quality. -- Cleaned up "to-do" comments referring to disposing memory on C#. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -- Added a fabric bot rule for localization issues. -- Fixed a CI build error after a .NET tools update. -- Update the Windows App SDK dependency version to 1.2. -- When building for arm64, the arm64 build tools are now preferred when building on an arm64 device. Thanks [@snickler](https://github.com/snickler)! -- Updated the C# test framework and removed unused Newtonsoft.Json package references. -- Updated StyleCop and fixed/enabled more warnings. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! -- Fixed a language typo in the code. Thanks [@eltociear](https://github.com/eltociear)! -- Improved code quality around some silent crashes that were being reported to Microsoft servers. -- Moved the GPO asset files to source instead of docs in the repo. -- Upgraded the unit test NuGet packages. +- Ignore spellcheck for MouseJumpUI/MainForm.resx file. (This was a hotfix for 0.67) +- Optimize versionAndSignCheck.ps1 script. Thanks [@snickler](https://github.com/snickler)! +- Upgraded NetAnalyzers to 7.0.1. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! +- Move all DLL imports in Settings project to NativeMethods.cs file. +- Fix FancyZones tools build issues. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! +- Centralize Logger used in C# projects. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! +- Add missing project references. Thanks [@ACGNnsj](https://github.com/ACGNnsj)! -#### What is being planned for version 0.67 +#### What is being planned for version 0.70 -For [v0.67][github-next-release-work], we'll work on below: +For [v0.70][github-next-release-work], we'll work on below: -- Allow installing without UAC. -- Add a flyout menu to the system tray icon for quick access. +- New utility: [PowerToys Peek](https://github.com/microsoft/PowerToys/issues/80) - Stability / bug fixes ## PowerToys Community @@ -199,5 +219,5 @@ The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has [usingPowerToys-docs-link]: https://aka.ms/powertoys-docs -[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F40 -[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F39 +[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F43 +[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aopen+is%3Aissue+project%3Amicrosoft%2FPowerToys%2F42 diff --git a/doc/devdocs/disk-usage-footprint.md b/doc/devdocs/disk-usage-footprint.md new file mode 100644 index 0000000000..08de4bd7bd --- /dev/null +++ b/doc/devdocs/disk-usage-footprint.md @@ -0,0 +1,82 @@ +# PowerToys disk usage footprint overview + +As of the v0.66 of PowerToys, core runtime dlls are being shipped self-contained as a part of PowerToys. During the installation process, hard-links are being created for every module that needs any of these shared libraries. + +## Why do this work? + +Our main motivations for doing are the following: + +1. Be able to install PowerToys without UAC elevation +2. PowerToys has a reduced disk space when components are shared. +3. Reduce additional downloads for dependencies + +## Current self-contained runtimes + +These dll's are installed in `/dll/` folder structure. + +- [Windows Application SDK runtime](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads) +- [.NET Desktop Runtime 7](https://dotnet.microsoft.com/download/dotnet/7.0) +- [Microsoft Visual C++ Runtime](https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist?view=msvc-170) + +## So why does PowerToys have a larger footprint now? + +Before the hard-link work was done, each of these runtimes had to be installed, so there were more items being installed outside just the PowerToys install directory. This space would not have been accounted. + +File Explorer treats hard-links the same as "original"/regular files/directories (https://learn.microsoft.com/troubleshoot/windows-server/backup-and-storage/disk-space-problems-on-ntfs-volumes#other-ntfs-features-that-may-cause-file-allocation-confusion), not as links. This results in File Explorer reporting size of PowerToys installation directory bigger than it is (more than 2GB). + +## How much space is actually being used + +As of v0.66 of PowerToys, the installed footprint is ~660MB. + +When we were prototyping and validating, here was our running table of results and why we took the route we did. + +| Moment | Installer size | App size | Installation dir size | Real size | Deps downloaded and installed during PT install | Total (installer size + real size + deps downloaded and installed during PT install) | +|---|---|---|---|---|---|---| +| v0.62.0.1
no deps self-contained | 125 MB | 817 MB | 529 MB | 534 MB | 418 MB | 1,077 MB | +| v0.63.0
WAS and VCRedist | 83 MB | 587 MB | 567 MB | 396 MB | 394 MB | 873 MB | +| v0.63.0
WAS1.2preview and VCRedist| 80 MB | 574 MB | 539 MB | 385 MB | 394 MB | 859 MB | +| Full Test
WAS, VCRedist, .NET | 149 MB | 705 MB | 1,760 MB | 557 MB | 0 MB | 706 MB | + +## Our process to verify current install footprint + +We created a quick virtual machine. We chose to use our fresh Windows 10 test virtual machines here. + +### Empty disk details + + + +### Install PowerToys to empty disk + + + +### PowerToys installation directory size shown by File Explorer + +As mentioned above, File Explorer shows size of PowerToys installation dir as every hard-link is a regular file for itself + + + +### PowerToys size shown by App->Installed apps + + + +### Disk usage with PowerToys installed + +Real disk usage of PowerToys is shown by inspecting disk usage after installing PowerToys. Used space is now 695MB, comparing to ~35MB used space for empty disk gives us the size of ~660MB for PowerToys installation dir. + + + +### PowerShell command calculating size of non-hardlinks files + +Size of regular files (non-hard-links) and hard-links can also be obtained by running following PowerShell command in PowerToys installation dir: + +``` +Regular files: +ls -Recurse -File -force -ErrorAction SilentlyContinue | ? LinkType -ne HardLink | Measure-Object -Property Length -Sum + +Hard-links +ls -Recurse -File -force -ErrorAction SilentlyContinue | ? LinkType -e HardLink | Measure-Object -Property Length -Sum +``` + +Running these commands for PowerToys v0.66 shows that size of regular files is way less than size of hard-links pointing to some of those regular files because PowerShell (same as File Explorer) also takes the size of hard-links as of regular files. + + diff --git a/doc/devdocs/logging.md b/doc/devdocs/logging.md index 57f2d13844..f98a03820a 100644 --- a/doc/devdocs/logging.md +++ b/doc/devdocs/logging.md @@ -1,9 +1,61 @@ -# How to use +# Logging -We use the awesome [spdlog](https://github.com/gabime/spdlog) library for logging as a git submodule under the `deps` directory. To use it in your project, just include [spdlog.props](../../deps/spdlog.props) in a .vcxproj like this: +Logging plays an important part in determining bugs in our code. It provides context for the developers about where and when errors occur. + +## Where are the logs saved + +* Most of the logs are saved under `%LOCALAPPDATA%/Microsoft/PowerToys`. +* For low-privilege processes (like preview handlers) the logs are saved under `%USERPROFILE%/AppData/LocalLow/Microsoft/PowerToys`. + +Logs are normally in a subfolder with the module name as title. + +The [BugReportTool](/tools/BugReportTool) will take logs from both locations when executed. + +## Using a logger in a project + +### Spdlog + +In C++ projects we use the awesome [spdlog](https://github.com/gabime/spdlog) library for logging as a git submodule under the `deps` directory. To use it in your project, just include [spdlog.props](/deps/spdlog.props) in a .vcxproj like this: ```xml ``` It'll add the required include dirs and link the library binary itself. +### PowerToys Logger in ManagedCommon + +For C# projects there is a static logger class in Managed Common called `Logger`. + +To use it, add a project reference to `ManagedCommon` and add the following line of code to all the files using the logger: + +```Csharp +using ManagedCommon; +``` + +In the `Main` function (or a function with a similar meaning (like `App` in a `App.xaml.cs` file)) you have to call `InitializeLogger` and specify the location where the logs will be saved (always use a path scheme similar to this example): + +```Csharp +Logger.InitializeLogger("\\FancyZones\\Editor\\Logs"); +``` + +For a low-privilege process you have to set the optional second parameter to `true`: + +```Csharp +Logger.InitializeLogger("\\FileExplorer\\Monaco\\Logs", true); +``` + +The `Logger` class contains the following logging functions: + +```Csharp +// Logs an error that the utility encountered +Logger.LogError(string message); +Logger.LogError(string message, Exception ex); +// Logs an error that isn't that grave +Logger.LogWarning(string message); +// Logs what the app is doing at the moment +Logger.LogInfo(string message); +// Like LogInfo just with infos important for debugging +Logger.LogDebug(string message); +// Logs the current state of the utility. +Logger.LogTrace(); +``` diff --git a/doc/devdocs/modules/launcher/plugins/calculator.md b/doc/devdocs/modules/launcher/plugins/calculator.md index 79ffe208a8..d9ac8e1e56 100644 --- a/doc/devdocs/modules/launcher/plugins/calculator.md +++ b/doc/devdocs/modules/launcher/plugins/calculator.md @@ -21,7 +21,12 @@ The Calculator plugin as the name suggests is used to perform calculations on th ### [`CalculateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) - The [`CalculateHelper.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) class checks to see if the user entered query is a valid input to the calculator and only if the input is valid does it perform the operation. -- It does so by matching the user query to a valid regex. + - It does so by matching the user query to a valid regex. +- This class also handles some human multiplication expression like `2(1+2)` and `(2+3)(3+4)` in order to be computed by `Mages` lib. + - It does so by matching some regex and inserting `'*'` where appropriate, e.g: `2(1+2) -> 2 * (1+2)` + - It takes into account the combination of numbers (`num`), constants (`const`), functions (`func`) and expressions in parentheses (`(exp)`). + - The blank spaces between them are also considered. + - Some combinations were not handled as they are not common such as `'const num'` or `'func const'` ### [`CalculateEngine`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs) - The main computation is done in the [`CalculateEngine.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs) file using the `Mages` library. diff --git a/doc/devdocs/modules/launcher/plugins/program.md b/doc/devdocs/modules/launcher/plugins/program.md index cc370258ec..1c3d86ba07 100644 --- a/doc/devdocs/modules/launcher/plugins/program.md +++ b/doc/devdocs/modules/launcher/plugins/program.md @@ -41,3 +41,7 @@ There are broadly two different categories of applications: ### Additional Notes - Arguments can be provided to the program plugin by entering them after `--` (a double dash). +- The localization is done using the `Localization Helper`from `Wox.Plugin.Common` hosted at runtime in a variable of plugin's main class. +- The `Run commands` differ in two points from the normal `Win32Programs`: + - The result title contains the executable type. + - The file types `.cpl` and `.msc` are supported too. diff --git a/doc/devdocs/modules/launcher/plugins/timeZone.md b/doc/devdocs/modules/launcher/plugins/timeZone.md deleted file mode 100644 index 49710cb947..0000000000 --- a/doc/devdocs/modules/launcher/plugins/timeZone.md +++ /dev/null @@ -1,141 +0,0 @@ -# Time Zone Plugin - -The Time Zone plugin allows users to search a time zone. - -## Special functions (differ from the regular functions) - -* Search for a country, like Kamchatka, Prince Edward Island, France -* Search for a shortcuts, like WEST, UTC, PST -* Search for a offset, like -12:00, -7, 5, 9:30 -* Search for a military time zone name (must activate in plugin settings) - -## How to add a new time zone or change one - -All time zones are located in `TimeZone.json` in root folder of the project. -The `TimeZone.json` use a JSON schema file that make it easier to edit it. - -| Key | Optional | Value type | -| ------------------- | -------- | ----------------- | -| `Offset` | **No** | String | -| `Name` | Yes | String | -| `MilitaryName` | Yes | String | -| `Shortcut` | Yes | String | -| `TimeNamesStandard` | Yes | List with strings | -| `TimeNamesDaylight` | Yes | List with strings | -| `ShortcutsStandard` | Yes | List with strings | -| `ShortcutsDaylight` | Yes | List with strings | -| `CountriesStandard` | Yes | List with strings | -| `CountriesDaylight` | Yes | List with strings | - -A minimum entry for the `TimeZone.json` looks like: - -```json - { - "Offset": "11:55", - "Name": "My crazy time zone", - } -``` - -A full entry for the `TimeZone.json` looks like: - -```json - { - "Offset": "11:55", - "Name": "My crazy time zone", - "Shortcut" : "MYTZ", - "MilitaryName" : "Order Time Zone", - "TimeNamesStandard": [ - "My crazy standard time" - ], - "ShortcutsStandard": [ - "MCST" - ], - "TimeNamesDaylight": [ - "My crazy daylight time" - ], - "ShortcutsDaylight": [ - "MCDT" - ], - "CountriesStandard": [ - "Crazy Land East" - ], - "CountriesDaylight": [ - "Crazy Land West" - ] - } -``` - -### Remarks - -* At minimum one of the optional value should be filled. - -## Scores - -* Scores are not used - -## Important for developers - -### General - -* The assembly name is cached into `_assemblyName` (to avoid to many calls of `Assembly.GetExecutingAssembly()`) - -## Microsoft.PowerToys.Run.Plugin.TimeZone project - -### Important plugin values (meta-data) - -| Name | Value | -| --------------- | ---------------------------------------------------- | -| ActionKeyword | `&` | -| ExecuteFileName | `Microsoft.PowerToys.Run.Plugin.TimeZone.dll` | -| ID | `BADD1B06EF0A4B61AD95395F24241D69` | - -### Interfaces used by this plugin - -The plugin use only these interfaces (all inside the `Main.cs`): - -* `Wox.Plugin.IPlugin` -* `Wox.Plugin.IContextMenu` -* `Wox.Plugin.IPluginI18n` -* `Wox.Plugin.ISettingProvider` -* `IDisposable` - -### Program files - -| File | Content | -| -------------------------------------- | ----------------------------------------------------------------------- | -| `Classes\TimeZoneProperties.cs` | A class that represent one time zone | -| `Classes\TimeZones.cs` | A wrapper class that only contains a list with time zones (see 1) | -| `Classes\TimeZoneSettings.cs` | A class that contains all settings for the Time Zone plugin | -| `Extensions\StringBuilderExtension.cs` | Extension methods for `StringBuilder` Objects | -| `Helper\ContextMenuHelper.cs` | All functions to build the context menu (for each result entry) | -| `Helper\JsonHelper.cs` | All functions to load the time zones from a JSON file | -| `Helper\ResultHelper.cs` | All functions to convert internal results into WOX results | -| `Helper\TranslationHelper.cs` | All functions to translate the result in the surface language | -| `Images\timeZone.dark.png` | Symbol for the results for the dark theme | -| `Images\timeZone.light.png` | Symbol for the results for the light theme | -| `Properties\Resources.Designer.resx` | File that contain all translatable keys | -| `Properties\Resources.resx` | File that contain all translatable strings in the neutral language | -| `Main.cs` | Main class, the only place that implement the WOX interfaces | -| `plugin.json` | All meta-data for this plugin | -| `timezones.json` | File that contains all time zone information | -| `timeZones.schema.json` | JSON schema for `timezones.json` | - -1. We need this extra wrapper class to make it possible that the JSON file can have and use a JSON schema file. -Because the JSON file must have a object as root type, instead of a array. - -### Important project values (*.csproj) - -| Name | Value | -| --------------- | ------------------------------------------------------------- | -| TargetFramework | `net6.0-windows` | -| Platforms | `x64` | -| Output | `..\..\..\..\..\x64\Debug\modules\launcher\Plugins\TimeZone\` | -| RootNamespace | `Microsoft.PowerToys.Run.Plugin.TimeZone` | -| AssemblyName | `Microsoft.PowerToys.Run.Plugin.TimeZone` | - -### Project dependencies - -#### Projects - -* `Wox.Infrastructure` -* `Wox.Plugin` diff --git a/doc/devdocs/modules/launcher/plugins/windowssettings.md b/doc/devdocs/modules/launcher/plugins/windowssettings.md index b057374472..9141afed97 100644 --- a/doc/devdocs/modules/launcher/plugins/windowssettings.md +++ b/doc/devdocs/modules/launcher/plugins/windowssettings.md @@ -62,6 +62,7 @@ A full entry for the `WindowsSettings.json` looks like: * The strings for `Name`, `AltNames`, `Areas`, `Type` and `Note` must not contain whitespace(s) or special characters (#, €, $, etc.) * The strings for `Name`, `AltNames`, `Areas`, `Type` and `Note` are used as ids for the resource file under `Properties\Resources.resx` * When you add new strings make sure you have added all translations for it. +* If a result has `mmc.exe` as command and the note property is filled, the note is shown in the sub title too. (This is for special MMC results where we don't have a .msc file.) ## Scores diff --git a/doc/devdocs/readme.md b/doc/devdocs/readme.md index 88b0867bd7..7c2a12061a 100644 --- a/doc/devdocs/readme.md +++ b/doc/devdocs/readme.md @@ -40,7 +40,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you 1. Windows 10 April 2018 Update (version 1803) or newer 1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer 1. Git clone PowerToys repository -1. Open started the `PowerToys.sln` +1. Open the `PowerToys.sln` file. 1. If you see a dialog that says `install extra components` in the solution explorer pane, click `install` ### Get Submodules to compile @@ -73,8 +73,8 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done ### Prerequisites for building the MSI installer 1. Install the [WiX Toolset Visual Studio 2022 Extension](https://marketplace.visualstudio.com/items?itemName=WixToolset.WixToolsetVisualStudio2022Extension). -1. Install the [WiX Toolset build tools](https://wixtoolset.org/releases/v3-14-0-6526/). The links to the binaries are not working, so we've created a [fork here](https://github.com/JaneaSystems/wix3/releases/tag/wix3-3.14.0.6526) where the WiX Toolset can be downloaded from. -1. Download [WiX binaries](https://github.com/JaneaSystems/wix3/releases/download/wix3-3.14.0.6526/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`. +1. Install the [WiX Toolset build tools](https://wixtoolset.org/docs/v3/releases/v3-14-0-6526/). (installer [direct link](https://wixtoolset.org/downloads/v3.14.0.6526/wix314.exe)) +1. Download [WiX binaries](https://wixtoolset.org/downloads/v3.14.0.6526/wix314-binaries.zip) and extract `wix.targets` to `C:\Program Files (x86)\WiX Toolset v3.14`. ### Locally building the installer prerequisite projects all at once from the command-line @@ -137,7 +137,7 @@ To run and debug PowerToys from Visual Studio when the user is a member of the A ## How to create new PowerToys See the instructions on [how to install the PowerToys Module project template](/tools/project_template).
-Specifications for the [PowerToys settings API](/doc/devdocs/settings.md). +Specifications for the [PowerToys settings API](settingsv2/readme.md). ## Implementation details diff --git a/doc/devdocs/tools/bug-report-tool.md b/doc/devdocs/tools/bug-report-tool.md new file mode 100644 index 0000000000..f73bdf3fc3 --- /dev/null +++ b/doc/devdocs/tools/bug-report-tool.md @@ -0,0 +1,31 @@ +# [Bug report tool](/tools/BugReportTool/) + +This tool is used to collect logs and system information for bug reports. The bug report is then saved as a zip file on the desktop. + +## Launching + +It can launch from the PowerToys tray icon by clicking "Report Bug", by clicking the bug report icon in the PowerToys flyout or by running the executable directly. + +## Included files + +The bug report includes the following files: + +* Settings files of the modules. +* Logs of the modules and the runner. +* Update log files. +* `compatibility-tab-info.txt` - Information about [compatibility settings](https://support.microsoft.com/windows/make-older-apps-or-programs-compatible-with-windows-783d6dd7-b439-bdb0-0490-54eea0f45938) set for certain PowerToys executables both in the user and system scope. +* `context-menu-packages.txt` - Information about the packages that are registered for the new Windows 11 context menu. +* `dotnet-installation-info.txt` - Information about the installed .NET versions. +* `EventViewer-*.xml` - These files contain event logs from the Windows Event Viewer for the executable specified in the file name. +* `gpo-configuration-info.txt` - Information about the configured [GPO](/doc/gpo/README.md). +* `installationFolderStructure.txt` - Information about the folder structure of the installation. All lines with files have the following structure: `FileName Version MD5Hash`. +* `last_version_run.json` - Information about the last version of PowerToys that was run. +* `log_settings.json` - Information about the log level settings. +* `monitor-report-info.txt` - Information about the monitors connected to the system. This file is created by the [monitor info report tool](/doc/devdocs/tools/monitor-info-report.md). +* `oobe_settings.json` - Information about the OOBE settings. +* `registry-report-info.txt` - Information about the registry keys that are used by PowerToys. +* `settings_placement.json` - Information about the placement of the settings window. +* `settings-telemetry.json` - Information about the last time telemetry data was sent. +* `UpdateState.json` - Information about the last update check and the current status of the update download. +* `windows-settings.txt` - Information about the Windows language settings. +* `windows-version.txt` - Information about the Windows version. diff --git a/doc/devdocs/tools/build-tools.md b/doc/devdocs/tools/build-tools.md new file mode 100644 index 0000000000..dfa4e251b2 --- /dev/null +++ b/doc/devdocs/tools/build-tools.md @@ -0,0 +1,31 @@ +# [Build tools](/tools/build/) + +These build tools help building PowerToys projects. + +## [build-essentials.ps1](/tools/build/build-essentials.ps1) + +A script that builds certain specified PowerToys projects. You can edit the `$ProjectsToBuild` variable to specify which projects to build. + +## [convert-resx-to-rc.ps1](/tools/build/convert-resx-to-rc.ps1) + +This script converts a .resx file to a .rc file, so it can be used in a C++ project. More information on localization can be found in the [localization guide](/doc/devdocs/localization.md). + +## [convert-stringtable-to-resx.ps1](/tools/build/convert-stringtable-to-resx.ps1) + +This script converts a stringtable to a .resx file, so it can be used in a C# project. More information about this script can be found in the [localization guide](/doc/devdocs/localization.md). + +## [move-and-rename-resx.ps1](/tools/build/move-and-rename-resx.ps1) + +This script is used by the pipeline to move the .resx files to the correct location, so that they can be localized into different languages. + +## [move-uwp-resw.ps1](/tools/build/move-uwp-resw.ps1) + +This script is used by the pipeline to move the .resw files to the correct location, so that they can be localized into different languages. + +## [versionSetting.ps1](/tools/build/versionSetting.ps1) + +Sets `version.props` file with the version number. + +## [video_conference_make_cab.ps1](/tools/build/video_conference_make_cab.ps1) + +This script creates a cab file for the Video Conference Mute driver. diff --git a/doc/devdocs/tools/clean-up-tool.md b/doc/devdocs/tools/clean-up-tool.md new file mode 100644 index 0000000000..f03eacdd32 --- /dev/null +++ b/doc/devdocs/tools/clean-up-tool.md @@ -0,0 +1,5 @@ +# [CleanUp_tool](/tools/CleanUp_tool/) and [CleanUp_tool_powershell_script](/tools/CleanUp_tool_powershell_script/CleanUp_tool.ps1) + +This tool, respective this powershell script, is used to clean up the PowerToys installation. It cleans the `AppData` folder and the registry. + +This tool is currently very outdated and just cleans up the registry keys of some few modules. diff --git a/doc/devdocs/tools/fancyzone-hit-test.md b/doc/devdocs/tools/fancyzone-hit-test.md new file mode 100644 index 0000000000..b49207faa0 --- /dev/null +++ b/doc/devdocs/tools/fancyzone-hit-test.md @@ -0,0 +1,5 @@ +# [FancyZone hit test tool](/tools/FancyZone_HitTest/) + +![Image of the FancyZones hit test tool](/doc/images/tools/fancyzone-hit-test.png) + +This tool tests the FancyZones layout selection logic. It displays a window with 5 zones. By hovering the mouse over the zones, the zone under the mouse cursor is highlighted. The sidebar shows different metrics that are used to determine which zone is under the mouse cursor. diff --git a/tools/FancyZones_DrawLayoutTest/README.md b/doc/devdocs/tools/fancyzones-draw-layout-test.md similarity index 86% rename from tools/FancyZones_DrawLayoutTest/README.md rename to doc/devdocs/tools/fancyzones-draw-layout-test.md index 652124f7c7..9f15a3a2ad 100644 --- a/tools/FancyZones_DrawLayoutTest/README.md +++ b/doc/devdocs/tools/fancyzones-draw-layout-test.md @@ -1,7 +1,7 @@ -## Testing tool for drawing zone layout +# [FancyZones_DrawLayoutTest](/tools/FancyZones_DrawLayoutTest/) This test tool is created in order to debug issues related to the drawing of zone layout on screen. Currently, only column layout is supported with modifiable number of zones. Pressing **w** key toggles zone appearance on primary screen (multi monitor support not yet in place). Pressing **q** key exits application. -Application is DPI unaware which means that application does not scale for DPI changes and it always assumes to have a scale factor of 100% (96 DPI). Scaling will be automatically performed by the system. \ No newline at end of file +Application is DPI unaware which means that application does not scale for DPI changes and it always assumes to have a scale factor of 100% (96 DPI). Scaling will be automatically performed by the system. diff --git a/doc/devdocs/tools/fancyzones-zonable-tester.md b/doc/devdocs/tools/fancyzones-zonable-tester.md new file mode 100644 index 0000000000..0b552f4636 --- /dev/null +++ b/doc/devdocs/tools/fancyzones-zonable-tester.md @@ -0,0 +1,13 @@ +# [FancyZones_zonable_tester](/tools/FancyZones_zonable_tester/) + +![Image of the FancyZones zonable tester](/doc/images/tools/fancyzones-zonable-tester.png) + +This command line application tests if the window where the mouse cursor is located is zonable. It also adds additional information about the window to the console output: + +* The HWND (window handle) of the window +* The process ID of the window +* The HWND of the window in the foreground +* The style of the window +* The exStyle of the window +* The window class +* The path of the process that created the window diff --git a/doc/devdocs/tools.md b/doc/devdocs/tools/monitor-info-report.md similarity index 89% rename from doc/devdocs/tools.md rename to doc/devdocs/tools/monitor-info-report.md index 2e14440c97..489f109a0e 100644 --- a/doc/devdocs/tools.md +++ b/doc/devdocs/tools/monitor-info-report.md @@ -1,10 +1,8 @@ -# Tools - -## [Monitor info report](tools\monitor_info_report) +# [Monitor info report tool](/tools/MonitorReportTool) A small diagnostic tool which helps identifying WinAPI bugs related to the physical monitor detection. When launched, it creates a log file like this: -``` +```text GetSystemMetrics = 2 GetMonitorInfo OK EnumDisplayDevices OK: @@ -20,4 +18,5 @@ EnumDisplayDevices OK: DeviceString = Generic PnP Monitor EnumDisplayMonitors OK ``` -and also duplicates the info to `stdout`. \ No newline at end of file + +and also duplicates the info to `stdout`. diff --git a/doc/devdocs/tools/readme.md b/doc/devdocs/tools/readme.md new file mode 100644 index 0000000000..8794b77e3e --- /dev/null +++ b/doc/devdocs/tools/readme.md @@ -0,0 +1,21 @@ +# PowerToys tools + +Tools in PowerToys are standalone apps and scripts that run outside of the PowerToys runner. They help developers and users to debug and test PowerToys features. + +The source code of the tools can be found in the [tools folder](/tools). The compiled tools are saved under `{PowerToysInstallPath}\tools`. + +## Overview of the tools + +Following tools are currently available: + +* [BugReportTool](bug-report-tool.md) - A tool to collect logs and system information for bug reports. +* [Build tools](build-tools.md) - A set of scripts that help building PowerToys. +* [Clean up tool](clean-up-tool.md) - A tool to clean up the PowerToys installation. +* [FancyZones hit test](fancyzone-hit-test.md) - A tool to test FancyZones layout selection logic. +* [FancyZones draw layout test](fancyzones-draw-layout-test.md) - A tool to test FancyZones layout drawing logic. +* [FancyZones zonable tester](fancyzones-zonable-tester.md) - A tool to test if a window is zonable. +* [Monitor info report](monitor-info-report.md) - A small diagnostic tool which helps identifying WinAPI bugs related to the physical monitor detection. +* [project template](/tools/project_template/README.md) - A Visual Studio project template for a new PowerToys project. +* [StylesReportTool](styles-report-tool.md) - A tool to collect information about an open window. +* [Verification scripts](verification-scripts.md) - A set of scripts that help verifying the PowerToys installation. +* [WebcamReportTool](webcam-report-tool.md) - A tool to collect information about the connected webcams. diff --git a/doc/devdocs/tools/styles-report-tool.md b/doc/devdocs/tools/styles-report-tool.md new file mode 100644 index 0000000000..bb31c9c98d --- /dev/null +++ b/doc/devdocs/tools/styles-report-tool.md @@ -0,0 +1,12 @@ +# [Styles Report Tool](/tools/StylesReportTool/) + +The Styles Report Tool is a tool to collect information about an open window. Run the tool, bring the window to the foreground and press Ctrl+Alt+S. The tool will create a file on your desktop called `window_styles.txt` with the information about the window. + +## Collected information + +* Process name +* Window class +* Window style parameters +* Window exStyle parameters +* DWM attributes +* Infos about the Virtual Desktop the window is on diff --git a/doc/devdocs/tools/verification-scripts.md b/doc/devdocs/tools/verification-scripts.md new file mode 100644 index 0000000000..cff58f478f --- /dev/null +++ b/doc/devdocs/tools/verification-scripts.md @@ -0,0 +1,25 @@ +# [Verification Scripts](/tools/Verification%20scripts/) + +This folder contains powershell scripts that help verifying the PowerToys installation. + +## [Check preview handler registration](/tools/Verification%20scripts/Check%20preview%20handler%20registration.ps1) + +This script checks the preview handler registration for the following file types: + +* .markdown +* .mdtext +* .mdtxt +* .mdown +* .mkdn +* .mdwn +* .mkd +* .md +* .svg +* .svgz +* .pdf +* .gcode +* .stl +* .txt +* .ini + +The tool shows the user handler and the machine handler for each file type and displays the App GUID of the corresponding handler. diff --git a/doc/devdocs/tools/webcam-report-tool.md b/doc/devdocs/tools/webcam-report-tool.md new file mode 100644 index 0000000000..efa6d47da3 --- /dev/null +++ b/doc/devdocs/tools/webcam-report-tool.md @@ -0,0 +1,6 @@ +# [WebcamReportTool](/tools/WebcamReportTool/) + +This command line application generates a report about the connected webcams on the desktop called "WebcamReport.txt". The report contains the following information about every webcam: + +* Name +* Supported formats diff --git a/doc/gpo/README.md b/doc/gpo/README.md index 482c3f6271..351ed5a376 100644 --- a/doc/gpo/README.md +++ b/doc/gpo/README.md @@ -31,3 +31,52 @@ If you enable this setting, the utility will be always enabled and the user won' If you disable this setting, the utility will be always disabled and the user won't be able to enable it. If you don't configure this setting, users are able to disable or enable the utility. + +### Allow experimentation + +This policy configures whether PowerToys experimentation is allowed. With experimentation allowed the user sees the new features being experimented if it gets selected as part of the test group. (Experimentation will only happen on Windows Insider builds.) + +If this setting is not configured or enabled, the user can control experimentation in the PowerToys settings menu. + +If this setting is disabled, experimentation is not allowed. + +If this setting is not configured, experimentation is allowed. + +### Installer and Updates + +#### Disable per-user installation + +This policy configures whether PowerToys per-user installation is allowed or not. + +If enabled, per-user installation is not allowed. + +If disabled or not configured, per-user installation is allowed. + +You can set this policy only as Computer policy. +#### Disable automatic downloads + +This policy configures whether automatic downloads of available updates are disabled or not. (On metered connections updates are never downloaded.) + +If enabled, automatic downloads are disabled. + +If disabled or not configured, the user is in control of automatic downloads setting. + +#### Suspend Action Center notification for new updates + +This policy configures whether the action center notification for new updates is suspended for 2 minor releases. (Example: if the installed version is v0.60.0, then the next notification is shown for the v0.63.* release.) + +If enabled, the notification is suspended. + +If disabled or not configured, the notification is shown. + +Note: The notification about new major versions is always displayed. + + diff --git a/doc/images/disk-usage/PowerToys_install_dir.png b/doc/images/disk-usage/PowerToys_install_dir.png new file mode 100644 index 0000000000..d8559a4b28 Binary files /dev/null and b/doc/images/disk-usage/PowerToys_install_dir.png differ diff --git a/doc/images/disk-usage/add_remove_size_v0.66.png b/doc/images/disk-usage/add_remove_size_v0.66.png new file mode 100644 index 0000000000..1792a77116 Binary files /dev/null and b/doc/images/disk-usage/add_remove_size_v0.66.png differ diff --git a/doc/images/disk-usage/empty_disk_details.png b/doc/images/disk-usage/empty_disk_details.png new file mode 100644 index 0000000000..376320830d Binary files /dev/null and b/doc/images/disk-usage/empty_disk_details.png differ diff --git a/doc/images/disk-usage/install_dir_size_v0.66.png b/doc/images/disk-usage/install_dir_size_v0.66.png new file mode 100644 index 0000000000..9a3038d937 Binary files /dev/null and b/doc/images/disk-usage/install_dir_size_v0.66.png differ diff --git a/doc/images/disk-usage/pwsh_v0.66.png b/doc/images/disk-usage/pwsh_v0.66.png new file mode 100644 index 0000000000..854c154c05 Binary files /dev/null and b/doc/images/disk-usage/pwsh_v0.66.png differ diff --git a/doc/images/disk-usage/used_disk_space_v0.66.png b/doc/images/disk-usage/used_disk_space_v0.66.png new file mode 100644 index 0000000000..af687d9e43 Binary files /dev/null and b/doc/images/disk-usage/used_disk_space_v0.66.png differ diff --git a/doc/images/icons/Paste As Plain Text.png b/doc/images/icons/Paste As Plain Text.png new file mode 100644 index 0000000000..3ce90ef52d Binary files /dev/null and b/doc/images/icons/Paste As Plain Text.png differ diff --git a/doc/images/icons/Registry Preview.png b/doc/images/icons/Registry Preview.png new file mode 100644 index 0000000000..ff2d58613c Binary files /dev/null and b/doc/images/icons/Registry Preview.png differ diff --git a/doc/images/tools/fancyzone-hit-test.png b/doc/images/tools/fancyzone-hit-test.png new file mode 100644 index 0000000000..8886b2be5f Binary files /dev/null and b/doc/images/tools/fancyzone-hit-test.png differ diff --git a/doc/images/tools/fancyzones-zonable-tester.png b/doc/images/tools/fancyzones-zonable-tester.png new file mode 100644 index 0000000000..4a10315456 Binary files /dev/null and b/doc/images/tools/fancyzones-zonable-tester.png differ diff --git a/installer/PowerToysSetup/AlwaysOnTop.wxs b/installer/PowerToysSetup/AlwaysOnTop.wxs new file mode 100644 index 0000000000..f98f84d987 --- /dev/null +++ b/installer/PowerToysSetup/AlwaysOnTop.wxs @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Awake.wxs b/installer/PowerToysSetup/Awake.wxs new file mode 100644 index 0000000000..7228069434 --- /dev/null +++ b/installer/PowerToysSetup/Awake.wxs @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/ColorPicker.wxs b/installer/PowerToysSetup/ColorPicker.wxs new file mode 100644 index 0000000000..088c94e19e --- /dev/null +++ b/installer/PowerToysSetup/ColorPicker.wxs @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Common.wxi b/installer/PowerToysSetup/Common.wxi new file mode 100644 index 0000000000..76dcb2ac9d --- /dev/null +++ b/installer/PowerToysSetup/Common.wxi @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Core.wxs b/installer/PowerToysSetup/Core.wxs new file mode 100644 index 0000000000..330ff1cd76 --- /dev/null +++ b/installer/PowerToysSetup/Core.wxs @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INSTALLDESKTOPSHORTCUT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/FancyZones.wxs b/installer/PowerToysSetup/FancyZones.wxs new file mode 100644 index 0000000000..1a29f8df40 --- /dev/null +++ b/installer/PowerToysSetup/FancyZones.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/FileExplorerPreview.wxs b/installer/PowerToysSetup/FileExplorerPreview.wxs new file mode 100644 index 0000000000..ba819ad22c --- /dev/null +++ b/installer/PowerToysSetup/FileExplorerPreview.wxs @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/FileLocksmith.wxs b/installer/PowerToysSetup/FileLocksmith.wxs new file mode 100644 index 0000000000..f2417ed0f0 --- /dev/null +++ b/installer/PowerToysSetup/FileLocksmith.wxs @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Hosts.wxs b/installer/PowerToysSetup/Hosts.wxs new file mode 100644 index 0000000000..5a63b632f3 --- /dev/null +++ b/installer/PowerToysSetup/Hosts.wxs @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/ImageResizer.wxs b/installer/PowerToysSetup/ImageResizer.wxs new file mode 100644 index 0000000000..5534e838c8 --- /dev/null +++ b/installer/PowerToysSetup/ImageResizer.wxs @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Images/logo.png b/installer/PowerToysSetup/Images/logo.png index d130d131bd..c9f9405405 100644 Binary files a/installer/PowerToysSetup/Images/logo.png and b/installer/PowerToysSetup/Images/logo.png differ diff --git a/installer/PowerToysSetup/Images/logo150.png b/installer/PowerToysSetup/Images/logo150.png index 7ff16cbc02..35e757d8e5 100644 Binary files a/installer/PowerToysSetup/Images/logo150.png and b/installer/PowerToysSetup/Images/logo150.png differ diff --git a/installer/PowerToysSetup/Images/logo44.png b/installer/PowerToysSetup/Images/logo44.png index 78b8ecf614..b931931dff 100644 Binary files a/installer/PowerToysSetup/Images/logo44.png and b/installer/PowerToysSetup/Images/logo44.png differ diff --git a/installer/PowerToysSetup/KeyboardManager.wxs b/installer/PowerToysSetup/KeyboardManager.wxs new file mode 100644 index 0000000000..925588c41e --- /dev/null +++ b/installer/PowerToysSetup/KeyboardManager.wxs @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/MeasureTool.wxs b/installer/PowerToysSetup/MeasureTool.wxs new file mode 100644 index 0000000000..879a3246ac --- /dev/null +++ b/installer/PowerToysSetup/MeasureTool.wxs @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/MonacoSRC.wxs b/installer/PowerToysSetup/MonacoSRC.wxs new file mode 100644 index 0000000000..b8a997ac44 --- /dev/null +++ b/installer/PowerToysSetup/MonacoSRC.wxs @@ -0,0 +1,1059 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/installer/PowerToysSetup/MouseUtils.wxs b/installer/PowerToysSetup/MouseUtils.wxs new file mode 100644 index 0000000000..933b6b115f --- /dev/null +++ b/installer/PowerToysSetup/MouseUtils.wxs @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PastePlain.wxs b/installer/PowerToysSetup/PastePlain.wxs new file mode 100644 index 0000000000..a0a92e4603 --- /dev/null +++ b/installer/PowerToysSetup/PastePlain.wxs @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerAccent.wxs b/installer/PowerToysSetup/PowerAccent.wxs new file mode 100644 index 0000000000..98d78ca856 --- /dev/null +++ b/installer/PowerToysSetup/PowerAccent.wxs @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerRename.wxs b/installer/PowerToysSetup/PowerRename.wxs new file mode 100644 index 0000000000..5b5f48f1e7 --- /dev/null +++ b/installer/PowerToysSetup/PowerRename.wxs @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/PowerToys.wxs b/installer/PowerToysSetup/PowerToys.wxs index 0818170273..bb4d820ba4 100644 --- a/installer/PowerToysSetup/PowerToys.wxs +++ b/installer/PowerToysSetup/PowerToys.wxs @@ -1,25 +1,13 @@ - - - - - - - - - - - - - - - + + + - + + + + + - + + + + - TargetPowerToysVersion >= DetectedPowerToysVersion OR WixBundleInstalled + + + + MinimumVersion >= DetectedPowerToysVersion + TargetPowerToysVersion >= DetectedPowerToysUserVersion OR WixBundleInstalled + + MinimumVersion >= DetectedPowerToysUserVersion + TargetPowerToysVersion >= DetectedPowerToysVersion OR WixBundleInstalled + @@ -61,7 +64,7 @@ Id="TerminatePowerToys" SourceFile="terminate_powertoys.cmd" Permanent="yes" - PerMachine="yes" + PerMachine="$(var.PerMachineYesNo)" Vital="no"> diff --git a/installer/PowerToysSetup/PowerToysBootstrapper.wixproj b/installer/PowerToysSetup/PowerToysBootstrapper.wixproj index f03cc88b35..ca71607595 100644 --- a/installer/PowerToysSetup/PowerToysBootstrapper.wixproj +++ b/installer/PowerToysSetup/PowerToysBootstrapper.wixproj @@ -1,4 +1,4 @@ - + PowerToysBootstrapper {31d72625-43c1-41b1-b784-bce4a8dc5543} + + $(DefineConstants);PerUser=true + + + $(DefineConstants);PerUser=false + Release x64 @@ -20,8 +26,10 @@ PowerToysSetup-$(Version)-$(Platform) Bundle True - PowerToysSetup-$(Version)-$(Platform) - $(Platform)\$(Configuration)\ + PowerToysSetup-$(Version)-$(Platform) + PowerToysUserSetup-$(Version)-$(Platform) + $(Platform)\$(Configuration)\MachineSetup + $(Platform)\$(Configuration)\UserSetup obj\$(Platform)\$(Configuration)\ diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index 4c8858c5c7..ef1e0af9ae 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -11,8 +11,8 @@ simulate the build pipeline doing that for you. --> IF NOT DEFINED IsPipeline ( call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.19041.0 -SET PTRoot=..\..\..\.. -call "..\..\publish.cmd" x64 +SET PTRoot=..\..\..\..\.. +call "..\..\..\publish.cmd" x64 ) @@ -20,20 +20,50 @@ call "..\..\publish.cmd" x64 Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\ARM64\$(Configuration)\common\FilePreviewCommon\monaco\monacoSRC IF NOT DEFINED IsPipeline ( call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=arm64 -host_arch=amd64 -winsdk=10.0.19041.0 -SET PTRoot=..\..\..\.. -call "..\..\publish.cmd" arm64 +SET PTRoot=..\..\..\..\.. +call "..\..\..\publish.cmd" arm64 ) Always - call move /Y ..\..\Product.wxs.bk ..\..\Product.wxs - call move /Y ..\..\..\PowerToysSetupCustomActions\DepsFilesLists.h.bk ..\..\..\PowerToysSetupCustomActions\DepsFilesLists.h + call move /Y ..\..\..\AlwaysOnTop.wxs.bk ..\..\..\AlwaysOnTop.wxs + call move /Y ..\..\..\Awake.wxs.bk ..\..\..\Awake.wxs + call move /Y ..\..\..\ColorPicker.wxs.bk ..\..\..\ColorPicker.wxs + call move /Y ..\..\..\Core.wxs.bk ..\..\..\Core.wxs + call move /Y ..\..\..\FancyZones.wxs.bk ..\..\..\FancyZones.wxs + call move /Y ..\..\..\FileExplorerPreview.wxs.bk ..\..\..\FileExplorerPreview.wxs + call move /Y ..\..\..\FileLocksmith.wxs.bk ..\..\..\FileLocksmith.wxs + call move /Y ..\..\..\Hosts.wxs.bk ..\..\..\Hosts.wxs + call move /Y ..\..\..\ImageResizer.wxs.bk ..\..\..\ImageResizer.wxs + call move /Y ..\..\..\KeyboardManager.wxs.bk ..\..\..\KeyboardManager.wxs + call move /Y ..\..\..\MeasureTool.wxs.bk ..\..\..\MeasureTool.wxs + call move /Y ..\..\..\MouseUtils.wxs.bk ..\..\..\MouseUtils.wxs + call move /Y ..\..\..\PowerAccent.wxs.bk ..\..\..\PowerAccent.wxs + call move /Y ..\..\..\PowerRename.wxs.bk ..\..\..\PowerRename.wxs + call move /Y ..\..\..\Product.wxs.bk ..\..\..\Product.wxs + call move /Y ..\..\..\RegistryPreview.wxs.bk ..\..\..\RegistryPreview.wxs + call move /Y ..\..\..\Resources.wxs.bk ..\..\..\Resources.wxs + call move /Y ..\..\..\Run.wxs.bk ..\..\..\Run.wxs + call move /Y ..\..\..\Settings.wxs.bk ..\..\..\Settings.wxs + call move /Y ..\..\..\ShortcutGuide.wxs.bk ..\..\..\ShortcutGuide.wxs + call move /Y ..\..\..\TextExtractor.wxs.bk ..\..\..\TextExtractor.wxs + call move /Y ..\..\..\Tools.wxs.bk ..\..\..\Tools.wxs + call move /Y ..\..\..\VideoConference.wxs.bk ..\..\..\VideoConference.wxs + call move /Y ..\..\..\WinAppSDK.wxs.bk ..\..\..\WinAppSDK.wxs + call move /Y ..\..\..\..\PowerToysSetupCustomActions\DepsFilesLists.h.bk ..\..\..\..\PowerToysSetupCustomActions\DepsFilesLists.h PowerToysInstaller + + $(DefineConstants);PerUser=true + + + $(DefineConstants);PerUser=false + + Release @@ -41,15 +71,22 @@ call "..\..\publish.cmd" arm64 3.10 022a9d30-7c4f-416d-a9df-5ff2661cc0ad 2.0 - PowerToysSetup-$(Version)-$(Platform) + PowerToysSetup-$(Version)-$(Platform) + PowerToysUserSetup-$(Version)-$(Platform) Package True + + + ICE91 + 1026;1076 - $(Platform)\$(Configuration)\ - obj\$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\MachineSetup + $(Platform)\$(Configuration)\UserSetup + obj\$(Platform)\$(Configuration)\MachineSetup + obj\$(Platform)\$(Configuration)\UserSetup ICE40 @@ -62,7 +99,34 @@ call "..\..\publish.cmd" arm64 + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -112,18 +176,19 @@ call "..\..\publish.cmd" arm64 - --> - + SuppressFragments="false" + SuppressRegistry="false" + SuppressRootDirectory="true"/> + --> +
\ No newline at end of file diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index c33f2157eb..66320a763e 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -2,34 +2,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + UpgradeCode="$(var.UpgradeCodeGUID)"> - + + - + @@ -185,19 +48,44 @@ + + + Description="Contains all PowerToys features."> - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + Installed AND NOT (_REMOVE_ALL="Yes") Installed AND NOT (_REMOVE_ALL="Yes") + @@ -230,13 +119,13 @@ - - + + - + ""]]> @@ -244,6 +133,7 @@ DEFAULTBOOTSTRAPPERINSTALLFOLDER OR PREVIOUSINSTALLFOLDER = ""]]> + @@ -258,6 +148,9 @@ NOT Installed and CREATESCHEDULEDTASK = 1 + + NOT Installed + NOT Installed @@ -291,9 +184,8 @@ Installed AND (REMOVE="ALL") - - Installed AND (REMOVE="ALL") - + + Installed AND (REMOVE="ALL") @@ -524,6 +416,13 @@ DllEntry="UnRegisterContextMenuPackagesCA" /> + + @@ -534,9 +433,10 @@ + - + @@ -560,13 +460,16 @@ + + + - - - + + + @@ -597,6 +500,19 @@ + + + + + + + + + + + + + @@ -629,83 +545,62 @@ - - - - - - - - - - - - - - - - - - - - - @@ -719,1754 +614,16 @@ - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WINDOWSBUILDNUMBER >= 19041 - - - - - - - - - - - - - - WINDOWSBUILDNUMBER >= 19041 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLDESKTOPSHORTCUT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/installer/PowerToysSetup/RegistryPreview.wxs b/installer/PowerToysSetup/RegistryPreview.wxs new file mode 100644 index 0000000000..74804f6c6e --- /dev/null +++ b/installer/PowerToysSetup/RegistryPreview.wxs @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Resources.wxs b/installer/PowerToysSetup/Resources.wxs new file mode 100644 index 0000000000..351bd05393 --- /dev/null +++ b/installer/PowerToysSetup/Resources.wxs @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Run.wxs b/installer/PowerToysSetup/Run.wxs new file mode 100644 index 0000000000..9d22b64156 --- /dev/null +++ b/installer/PowerToysSetup/Run.wxs @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Settings.wxs b/installer/PowerToysSetup/Settings.wxs new file mode 100644 index 0000000000..698575cbcf --- /dev/null +++ b/installer/PowerToysSetup/Settings.wxs @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/ShortcutGuide.wxs b/installer/PowerToysSetup/ShortcutGuide.wxs new file mode 100644 index 0000000000..2053ffae34 --- /dev/null +++ b/installer/PowerToysSetup/ShortcutGuide.wxs @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/TextExtractor.wxs b/installer/PowerToysSetup/TextExtractor.wxs new file mode 100644 index 0000000000..3afbb2a5e1 --- /dev/null +++ b/installer/PowerToysSetup/TextExtractor.wxs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Tools.wxs b/installer/PowerToysSetup/Tools.wxs new file mode 100644 index 0000000000..a9fe0bf306 --- /dev/null +++ b/installer/PowerToysSetup/Tools.wxs @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/VideoConference.wxs b/installer/PowerToysSetup/VideoConference.wxs new file mode 100644 index 0000000000..c2b6915e5c --- /dev/null +++ b/installer/PowerToysSetup/VideoConference.wxs @@ -0,0 +1,61 @@ + + + + + + + + + + WINDOWSBUILDNUMBER >= 19041 + + + + + + + + + + + + + + + + + WINDOWSBUILDNUMBER >= 19041 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/WinAppSDK.wxs b/installer/PowerToysSetup/WinAppSDK.wxs new file mode 100644 index 0000000000..cb20c8cfa9 --- /dev/null +++ b/installer/PowerToysSetup/WinAppSDK.wxs @@ -0,0 +1,514 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/generateAllFileComponents.ps1 b/installer/PowerToysSetup/generateAllFileComponents.ps1 new file mode 100644 index 0000000000..93b35f7684 --- /dev/null +++ b/installer/PowerToysSetup/generateAllFileComponents.ps1 @@ -0,0 +1,214 @@ +[CmdletBinding()] +Param( + [Parameter(Mandatory = $True, Position = 1)] + [string]$platform, + [Parameter(Mandatory = $False, Position = 2)] + [string]$installscopeperuser = "false" +) + +if ($platform -ceq "arm64") { + $platform = "ARM64" +} + +if ($installscopeperuser -eq "true") { + $registryroot = "HKCU" +} else { + $registryroot = "HKLM" +} + +#AlwaysOnTop +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName AlwaysOnTopFiles -wxsFilePath $PSScriptRoot\AlwaysOnTop.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\AlwaysOnTop""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""AlwaysOnTopFiles"" -wxsFilePath $PSScriptRoot\AlwaysOnTop.wxs -regroot $registryroot" + +#AwakeFiles +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\Awake\PowerToys.Awake.deps.json"" -fileListName AwakeFiles -wxsFilePath $PSScriptRoot\Awake.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName AwakeImagesFiles -wxsFilePath $PSScriptRoot\Awake.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\Awake\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""AwakeFiles"" -wxsFilePath $PSScriptRoot\Awake.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""AwakeImagesFiles"" -wxsFilePath $PSScriptRoot\Awake.wxs -regroot $registryroot" + +#ColorPicker +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json"" -fileListName ColorPickerFiles -wxsFilePath $PSScriptRoot\ColorPicker.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ColorPickerResourcesFiles -wxsFilePath $PSScriptRoot\ColorPicker.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\ColorPicker\Resources""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ColorPickerFiles"" -wxsFilePath $PSScriptRoot\ColorPicker.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ColorPickerResourcesFiles"" -wxsFilePath $PSScriptRoot\ColorPicker.wxs -regroot $registryroot" + +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json"" -fileListName ColorPickerFiles -wxsFilePath $PSScriptRoot\ColorPicker.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ColorPickerFiles"" -wxsFilePath $PSScriptRoot\ColorPicker.wxs -regroot $registryroot" + +#FancyZones +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\FancyZones\PowerToys.FancyZonesEditor.deps.json"" -fileListName FancyZonesFiles -wxsFilePath $PSScriptRoot\FancyZones.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""FancyZonesFiles"" -wxsFilePath $PSScriptRoot\FancyZones.wxs -regroot $registryroot" + +#FileExplorerAdd-ons +#TODO: There are multiple .deps.json files, make sure it works as expected +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\FileExplorerPreview\PowerToys.SvgThumbnailProvider.deps.json"" -fileListName PowerPreviewFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName MonacoCustomLanguagesFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\FileExplorerPreview\customLanguages""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""PowerPreviewFiles"" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""MonacoCustomLanguagesFiles"" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot" + +#FileLocksmith +#TODO: There are multiple .deps.json files, make sure it works as expected +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\FileLocksmith\PowerToys.FileLocksmithUI.deps.json"" -fileListName FileLocksmithFiles -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -isWinAppSdkProj 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName FileLocksmithAssetsFiles -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\FileLocksmith\Assets\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""FileLocksmithFiles"" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""FileLocksmithAssetsFiles"" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -regroot $registryroot" + +#Hosts +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\Hosts\PowerToys.Hosts.deps.json"" -fileListName HostsFiles -wxsFilePath $PSScriptRoot\Hosts.wxs -isWinAppSdkProj 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName HostsAssetsFiles -wxsFilePath $PSScriptRoot\Hosts.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\Hosts\Assets\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""HostsFiles"" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""HostsAssetsFiles"" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot" + +#ImageResizer +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\ImageResizer\PowerToys.ImageResizer.deps.json"" -fileListName ImageResizerFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\ImageResizer\Assets\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ImageResizerFiles"" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ImageResizerAssetsFiles"" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot" + +#MouseUtils +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName MouseUtilsFiles -wxsFilePath $PSScriptRoot\MouseUtils.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\MouseUtils\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""MouseUtilsFiles"" -wxsFilePath $PSScriptRoot\MouseUtils.wxs -regroot $registryroot" +#MouseJumpUI +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\MouseUtils\MouseJumpUI\PowerToys.MouseJumpUI.deps.json"" -fileListName MouseJumpUIFiles -wxsFilePath $PSScriptRoot\MouseUtils.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""MouseJumpUIFiles"" -wxsFilePath $PSScriptRoot\MouseUtils.wxs -regroot $registryroot" + +#MeasureTool +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\MeasureTool\PowerToys.MeasureToolUI.deps.json"" -fileListName MeasureToolFiles -wxsFilePath $PSScriptRoot\MeasureTool.wxs -isWinAppSdkProj 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""MeasureToolFiles"" -wxsFilePath $PSScriptRoot\MeasureTool.wxs -regroot $registryroot" + +#PowerAccent +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\PowerAccent\PowerToys.PowerAccent.deps.json"" -fileListName PowerAccentFiles -wxsFilePath $PSScriptRoot\PowerAccent.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""PowerAccentFiles"" -wxsFilePath $PSScriptRoot\PowerAccent.wxs -regroot $registryroot" + +#PowerRename +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName PowerRenameFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\PowerRename\"" -isWinAppSdkProj 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName PowerRenameAssetsFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\PowerRename\Assets\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""PowerRenameFiles"" -wxsFilePath $PSScriptRoot\PowerRename.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""PowerRenameAssetsFiles"" -wxsFilePath $PSScriptRoot\PowerRename.wxs -regroot $registryroot" + +#RegistryPreview +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\RegistryPreview\PowerToys.RegistryPreview.deps.json"" -fileListName RegistryPreviewFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName RegistryPreviewAssetsFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\RegistryPreview\Assets\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""RegistryPreviewFiles"" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""RegistryPreviewAssetsFiles"" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -regroot $registryroot" + +#Run +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\PowerToys.PowerLauncher.deps.json"" -fileListName launcherFiles -wxsFilePath $PSScriptRoot\Run.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName launcherImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""launcherFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""launcherImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +## Plugins +###Calculator +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Calculator\Microsoft.PowerToys.Run.Plugin.Calculator.deps.json"" -fileListName calcComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName calcImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Calculator\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""calcComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""calcImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Folder +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Folder\Microsoft.Plugin.Folder.deps.json"" -fileListName FolderComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName FolderImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Folder\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""FolderComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""FolderImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Program +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Program\Microsoft.Plugin.Program.deps.json"" -fileListName ProgramComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ProgramImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Program\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ProgramComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ProgramImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Shell +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Shell\Microsoft.Plugin.Shell.deps.json"" -fileListName ShellComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ShellImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Shell\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ShellComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ShellImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Indexer +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Indexer\Microsoft.Plugin.Indexer.deps.json"" -fileListName IndexerComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName IndexerImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Indexer\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""IndexerComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""IndexerImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###UnitConverter +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.deps.json"" -fileListName UnitConvCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName UnitConvImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\UnitConverter\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""UnitConvCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""UnitConvImagesCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###WebSearch +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WebSearch\Community.PowerToys.Run.Plugin.WebSearch.deps.json"" -fileListName WebSrchCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName WebSrchImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WebSearch\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WebSrchCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WebSrchImagesCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###History +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\History\Microsoft.PowerToys.Run.Plugin.History.deps.json"" -fileListName HistoryPluginComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName HistoryPluginImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\History\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""HistoryPluginComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""HistoryPluginImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Uri +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Uri\Microsoft.Plugin.Uri.deps.json"" -fileListName UriComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName UriImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Uri\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""UriComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""UriImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###VSCodeWorkspaces +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.deps.json"" -fileListName VSCWrkCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName VSCWrkImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\VSCodeWorkspaces\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""VSCWrkCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""VSCWrkImagesCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###WindowWalker +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WindowWalker\Microsoft.Plugin.WindowWalker.deps.json"" -fileListName WindowWlkrCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName WindowWlkrImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WindowWalker\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WindowWlkrCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WindowWlkrImagesCompFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###OneNote +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\OneNote\Microsoft.PowerToys.Run.Plugin.OneNote.deps.json"" -fileListName OneNoteComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName OneNoteImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\OneNote\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""OneNoteComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""OneNoteImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Registry +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Registry\Microsoft.PowerToys.Run.Plugin.Registry.deps.json"" -fileListName RegistryComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName RegistryImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Registry\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""RegistryComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""RegistryImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###Service +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Service\Microsoft.PowerToys.Run.Plugin.Service.deps.json"" -fileListName ServiceComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ServiceImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\Service\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ServiceComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ServiceImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###System +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\System\Microsoft.PowerToys.Run.Plugin.System.deps.json"" -fileListName SystemComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName SystemImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\System\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SystemComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SystemImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###TimeDate +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.deps.json"" -fileListName TimeDateComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName TimeDateImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\TimeDate\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""TimeDateComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""TimeDateImagesComponentFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###WindowsSettings +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json"" -fileListName WinSetCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName WinSetImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WindowsSettings\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WinSetCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WinSetImagesCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +###WindowsTerminal +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WindowsTerminal\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.deps.json"" -fileListName WinTermCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName WinTermImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\launcher\Plugins\WindowsTerminal\Images""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WinTermCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WinTermImagesCmpFiles"" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot" +## Plugins + +#ShortcutGuide +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\modules\ShortcutGuide\ShortcutGuide\svgs\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""ShortcutGuideSvgFiles"" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot" + +#TextExtractor +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\modules\PowerOCR\PowerToys.PowerOCR.deps.json"" -fileListName TextExtractorFiles -wxsFilePath $PSScriptRoot\TextExtractor.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""TextExtractorFiles"" -wxsFilePath $PSScriptRoot\TextExtractor.wxs -regroot $registryroot" + +#Settings +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson ""$PSScriptRoot..\..\..\$platform\Release\Settings\PowerToys.Settings.deps.json"" -fileListName SettingsV2Files -wxsFilePath $PSScriptRoot\Settings.wxs" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\Settings\Assets\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName SettingsV2AssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\Settings\Assets\Modules\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName SettingsV2OOBEAssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\Settings\Assets\Modules\OOBE\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName SettingsV2OOBEAssetsFluentIconsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\Settings\Assets\FluentIcons\""" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SettingsV2Files"" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SettingsV2AssetsFiles"" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SettingsV2AssetsModulesFiles"" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SettingsV2OOBEAssetsModulesFiles"" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot" +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""SettingsV2OOBEAssetsFluentIconsFiles"" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot" + +#WinAppSdk +Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WinAppSDKFiles"" -wxsFilePath $PSScriptRoot\WinAppSDK.wxs -regroot $registryroot" diff --git a/installer/PowerToysSetup/generateFileComponents.ps1 b/installer/PowerToysSetup/generateFileComponents.ps1 new file mode 100644 index 0000000000..ad223711e8 --- /dev/null +++ b/installer/PowerToysSetup/generateFileComponents.ps1 @@ -0,0 +1,56 @@ +[CmdletBinding()] +Param( + [Parameter(Mandatory = $True, Position = 1)] + [string]$fileListName, + [Parameter(Mandatory = $True, Position = 2)] + [string]$wxsFilePath, + [Parameter(Mandatory = $True, Position = 3)] + [string]$regroot +) + +$wxsFile = Get-Content $wxsFilePath; + +$wxsFile | ForEach-Object { + if ($_ -match "(") { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList', + Justification = 'variable is used in another scope')] + + $fileList = $matches[2] -split ';' + return + } +} + +$componentId = "$($fileListName)_Component" + +$componentDefs = "`r`n" +$componentDefs += +@" + + + + `r`n +"@ + +foreach ($file in $fileList) { + $fileTmp = $file -replace "-", "_" + $componentDefs += +@" + `r`n +"@ +} + +$componentDefs += +@" + `r`n +"@ + +$wxsFile = $wxsFile -replace "\s+()", $componentDefs + +$componentRef = +@" + +"@ + +$wxsFile = $wxsFile -replace "\s+()", "$componentRef`r`n " + +Set-Content -Path $wxsFilePath -Value $wxsFile \ No newline at end of file diff --git a/installer/PowerToysSetup/generateFileList.ps1 b/installer/PowerToysSetup/generateFileList.ps1 new file mode 100644 index 0000000000..7e0d8548a0 --- /dev/null +++ b/installer/PowerToysSetup/generateFileList.ps1 @@ -0,0 +1,84 @@ +[CmdletBinding()] +Param( + [Parameter(Mandatory = $True, Position = 1)] + [AllowEmptyString()] + [string]$fileDepsJson, + [Parameter(Mandatory = $True, Position = 2)] + [string]$fileListName, + [Parameter(Mandatory = $True, Position = 3)] + [string]$wxsFilePath, + # If there is no deps.json file, just pass path to files + [Parameter(Mandatory = $False, Position = 4)] + [string]$depsPath, + # launcher plugins are being loaded into launcher process, + # so there are some additional dependencies to skip + [Parameter(Mandatory = $False, Position = 5)] + [bool]$isLauncherPlugin, + # Skip winAppSDK dlls as those are hard-linked + [Parameter(Mandatory = $False, Position = 6)] + [bool]$isWinAppSdkProj +) + +$fileWxs = Get-Content $wxsFilePath; + +#Skip PowerToysInterop files +$coreWxs = Get-Content $PSScriptRoot/"Core.wxs" +$coreWxs | ForEach-Object { + if ($_ -match "(") { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList', + Justification = 'variable is used in another scope')] + + $interopFilesList = $matches[2] -split ';' + return + } +} + +#Skip WinAppSdk files +if ($isWinAppSdkProj -eq $True) { + $winAppSDKWxs = Get-Content $PSScriptRoot/"WinAppSDK.wxs" + $winAppSDKWxs | ForEach-Object { + if ($_ -match "(") { + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList', + Justification = 'variable is used in another scope')] + + $winAppSDKfilesList = $matches[2] -split ';' + return + } + } +} + +$fileExclusionList = @("*Test*", "*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe") + $interopFilesList + $winAppSDKfilesList + +$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*png", "*gif", "*ico", "*cur", "*svg", "index.html", "reg.js", "monacoSpecialLanguages.js", "resources.pri", "NLog.config") + +$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll") + +if ($fileDepsJson -eq [string]::Empty) { + $fileDepsRoot = $depsPath +} else { + $fileDepsRoot = (Get-ChildItem $fileDepsJson).Directory.FullName + $depsJson = Get-Content $fileDepsJson | ConvertFrom-Json + + $runtimeList = ([array]$depsJson.targets.PSObject.Properties)[-1].Value.PSObject.Properties | Where-Object { + $_.Name -match "runtimepack.*Runtime" + }; + + $runtimeList | ForEach-Object { + $_.Value.PSObject.Properties.Value | ForEach-Object { + $fileExclusionList += $_.PSObject.Properties.Name + } + } +} + +$fileExclusionList = $fileExclusionList | Where-Object {$_ -notin $dllsToIgnore} + +if ($isLauncherPlugin -eq $True) { + $fileInclusionList += @("*.deps.json") + $fileExclusionList += @("Ijwhost.dll", "PowerToys.Common.UI.dll", "PowerToys.GPOWrapper.dll", "PowerToys.GPOWrapperProjection.dll", "PowerToys.PowerLauncher.Telemetry.dll", "PowerToys.ManagedCommon.dll", "PowerToys.Settings.UI.Lib.dll", "Wox.Infrastructure.dll", "Wox.Plugin.dll") +} + +$fileList = Get-ChildItem $fileDepsRoot -Include $fileInclusionList -Exclude $fileExclusionList -File -Name + +$fileWxs = $fileWxs -replace "(<\?define $($fileListName)=)", " #include "../../src/common/logger/logger.h" +#include "../../src/common/utils/gpo.h" #include "../../src/common/utils/MsiUtils.h" #include "../../src/common/utils/modulesRegistry.h" #include "../../src/common/updating/installer.h" @@ -34,24 +35,6 @@ const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0' static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"; -struct WcaSink : spdlog::sinks::base_sink -{ - virtual void sink_it_(const spdlog::details::log_msg& msg) override - { - WcaLog(LOGMSG_STANDARD, msg.payload.data()); - } - virtual void flush_() override - { - // we don't need to flush wca log manually - } -}; - -void initSystemLogger() -{ - static std::once_flag initLoggerFlag; - std::call_once(initLoggerFlag, []() { Logger::init(std::vector{ std::make_shared() }); }); -} - HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir) { DWORD len = 0; @@ -68,9 +51,35 @@ HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir) LExit: return hr; } + +UINT __stdcall CheckGPOCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + + hr = WcaInitialize(hInstall, "CheckGPOCA"); + ExitOnFailure(hr, "Failed to initialize"); + + LPWSTR currentScope = nullptr; + hr = WcaGetProperty(L"InstallScope", ¤tScope); + + if(std::wstring{ currentScope } == L"perUser") + { + if (powertoys_gpo::getDisablePerUserInstallationValue() == powertoys_gpo::gpo_rule_configured_enabled) + { + PMSIHANDLE hRecord = MsiCreateRecord(0); + MsiRecordSetString(hRecord, 0, TEXT("The system administrator has disabled per-user installation.")); + MsiProcessMessage(hInstall, static_cast(INSTALLMESSAGE_ERROR + MB_OK), hRecord); + hr = E_ABORT; + } + } + +LExit: + UINT er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall) { - initSystemLogger(); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; std::wstring installationFolder; @@ -85,14 +94,14 @@ UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall) { if (!changeSet.apply()) { - WcaLog(LOGMSG_STANDARD, "Couldn't apply registry changeSet"); + Logger::error(L"Couldn't apply registry changeSet"); failedToApply = true; } } if (!failedToApply) { - WcaLog(LOGMSG_STANDARD, "All registry changeSets applied successfully"); + Logger::info(L"All registry changeSets applied successfully"); } LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; @@ -101,7 +110,6 @@ LExit: UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall) { - initSystemLogger(); HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; std::wstring installationFolder; @@ -131,14 +139,14 @@ UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall) if (auto msix = RcResource::create(IDR_BIN_MSIX_HELLO_PACKAGE, L"BIN", DLL_HANDLE)) { - WcaLog(LOGMSG_STANDARD, "Extracted MSIX"); + Logger::info(L"Extracted MSIX"); // TODO: Use to activate embedded MSIX const auto msix_path = std::filesystem::temp_directory_path() / "hello_package.msix"; if (!msix->saveAsFile(msix_path)) { ExitOnFailure(hr, "Failed to save msix"); } - WcaLog(LOGMSG_STANDARD, "Saved MSIX"); + Logger::info(L"Saved MSIX"); using namespace winrt::Windows::Management::Deployment; using namespace winrt::Windows::Foundation; @@ -150,7 +158,7 @@ UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall) ExitOnFailure(hr, "Failed to AddPackage"); } - WcaLog(LOGMSG_STANDARD, "MSIX[s] were installed!"); + Logger::info(L"MSIX[s] were installed!"); } else { @@ -181,11 +189,11 @@ UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall) auto result = pm.RemovePackageAsync(p.Id().FullName()).get(); if (result) { - WcaLog(LOGMSG_STANDARD, "MSIX was uninstalled!"); + Logger::info(L"MSIX was uninstalled!"); } else { - WcaLog(LOGMSG_STANDARD, "Couldn't uninstall MSIX!"); + Logger::error(L"Couldn't uninstall MSIX!"); } } @@ -227,7 +235,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); ExitOnFailure(hr, "Failed to initialize"); - WcaLog(LOGMSG_STANDARD, "Initialized."); + Logger::info(L"CreateScheduledTaskCA Initialized."); // ------------------------------------------------------ // Get the Domain/Username for the trigger. @@ -246,7 +254,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) wcscat_s(username_domain, L"\\"); wcscat_s(username_domain, username); - WcaLog(LOGMSG_STANDARD, "Current user detected: %ls", username_domain); + Logger::info(L"Current user detected: {}", username_domain); // Task Name. wstrTaskName = L"Autorun for "; @@ -264,7 +272,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -286,7 +294,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) pRootFolder->Release(); ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr); } - WcaLog(LOGMSG_STANDARD, "PowerToys task folder created."); + Logger::info(L"PowerToys task folder created."); } // If the same task exists, remove it. @@ -334,7 +342,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1")); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put the trigger ID: %x", hr); + Logger::error(L"Cannot put the trigger ID: {}", hr); } // Timing issues may make explorer not be started when the task runs. @@ -342,7 +350,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S")); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put the trigger delay: %x", hr); + Logger::error(L"Cannot put the trigger delay: {}", hr); } // Define the user. The task will execute when the user logs on. @@ -383,19 +391,19 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) hr = pPrincipal->put_Id(_bstr_t(L"Principal1")); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put the principal ID: %x", hr); + Logger::error(L"Cannot put the principal ID: {}", hr); } hr = pPrincipal->put_UserId(_bstr_t(username_domain)); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put principal user Id: %x", hr); + Logger::error(L"Cannot put principal user Id: {}", hr); } hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); if (FAILED(hr)) { - WcaLogError(hr, "Cannot put principal logon type: %x", hr); + Logger::error(L"Cannot put principal logon type: {}", hr); } // Run the task with the highest available privileges. @@ -419,7 +427,7 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) ExitOnFailure(hr, "Error saving the Task : %x", hr); } - WcaLog(LOGMSG_STANDARD, "Scheduled task created for the current user."); + Logger::info(L"Scheduled task created for the current user."); LExit: ReleaseStr(wszExecutablePath); @@ -480,7 +488,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA"); ExitOnFailure(hr, "Failed to initialize"); - WcaLog(LOGMSG_STANDARD, "Initialized."); + Logger::info(L"RemoveScheduledTasksCA Initialized."); // COM and Security Initialization is expected to have been done by the MSI. // It couldn't be done in the DLL, anyway. @@ -490,7 +498,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -503,7 +511,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) if (FAILED(hr)) { // Folder doesn't exist. No need to delete anything. - WcaLog(LOGMSG_STANDARD, "The PowerToys scheduled task folder wasn't found. Nothing to delete."); + Logger::info(L"The PowerToys scheduled task folder wasn't found. Nothing to delete."); hr = S_OK; ExitFunction(); } @@ -529,19 +537,19 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) hr = pTaskFolder->DeleteTask(taskName, 0); if (FAILED(hr)) { - WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr); + Logger::error(L"Cannot delete the {} task: {}", taskName, hr); } SysFreeString(taskName); } else { - WcaLogError(hr, "Cannot get the registered task name: %x", hr); + Logger::error(L"Cannot get the registered task name: {}", hr); } pRegisteredTask->Release(); } else { - WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr); + Logger::error(L"Cannot get the registered task item at index={}: {}", i + 1, hr); } } @@ -553,7 +561,7 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) pRootFolder->Release(); ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr); - WcaLog(LOGMSG_STANDARD, "Deleted the PowerToys Task Scheduler folder."); + Logger::info(L"Deleted the PowerToys Task Scheduler folder."); LExit: if (pService) @@ -754,9 +762,13 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall) UINT er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA"); MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", L""); + + LPWSTR currentScope = nullptr; + hr = WcaGetProperty(L"InstallScope", ¤tScope); + try { - if (auto install_path = GetMsiPackageInstalledPath()) + if (auto install_path = GetMsiPackageInstalledPath(std::wstring{ currentScope } == L"perUser")) { MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data()); } @@ -809,7 +821,7 @@ UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall) ExitOnFailure(hr, "Certificate file size not valid", hr); } - pFileContent = (char*)malloc(size); + pFileContent = static_cast(malloc(size)); DWORD sizeread; if (!ReadFile(hfile, pFileContent, size, &sizeread, nullptr)) @@ -820,7 +832,7 @@ UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall) if (!CertAddEncodedCertificateToStore(hCertStore, X509_ASN_ENCODING, - (const BYTE*)pFileContent, + reinterpret_cast(pFileContent), size, CERT_STORE_ADD_ALWAYS, nullptr)) @@ -1004,6 +1016,7 @@ const std::wstring WinAppSDKConsumers[] = L"modules\\MeasureTool", L"modules\\FileLocksmith", L"modules\\Hosts", + L"modules\\RegistryPreview", L"modules\\Peek", }; @@ -1058,6 +1071,8 @@ const std::wstring PTInteropConsumers[] = L"modules\\Hosts", L"modules\\Peek", L"modules\\FileExplorerPreview", + L"modules\\MouseUtils\\MouseJumpUI", + L"modules\\RegistryPreview", }; UINT __stdcall CreatePTInteropHardlinksCA(MSIHANDLE hInstall) @@ -1101,7 +1116,8 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; std::wstring installationFolder, dotnetRuntimeFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, - imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir, peekDir; + imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir, + mouseJumpDir, registryPreviewDir, peekDir; hr = WcaInitialize(hInstall, "CreateDotnetRuntimeHardlinksCA"); ExitOnFailure(hr, "Failed to initialize"); @@ -1122,6 +1138,8 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\"; hostsDir = installationFolder + L"modules\\Hosts\\"; fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\"; + mouseJumpDir = installationFolder + L"modules\\MouseUtils\\MouseJumpUI\\"; + registryPreviewDir = installationFolder + L"modules\\RegistryPreview\\"; peekDir = installationFolder + L"modules\\Peek\\"; for (auto file : dotnetRuntimeFiles) @@ -1139,6 +1157,8 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileLocksmithDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (mouseJumpDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (registryPreviewDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (peekDir + file).c_str(), ec); if (ec.value() != S_OK) @@ -1163,6 +1183,8 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (powerAccentDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec); std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (mouseJumpDir + file).c_str(), ec); + std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (registryPreviewDir + file).c_str(), ec); if (ec.value() != S_OK) { @@ -1256,8 +1278,8 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; std::wstring installationFolder, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir, - imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, peekDir, - hostsDir, fileLocksmithDir; + imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, + hostsDir, fileLocksmithDir, mouseJumpDir, registryPreviewDir, peekDir; hr = WcaInitialize(hInstall, "DeleteDotnetRuntimeHardlinksCA"); ExitOnFailure(hr, "Failed to initialize"); @@ -1277,6 +1299,8 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\"; hostsDir = installationFolder + L"modules\\Hosts\\"; fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\"; + mouseJumpDir = installationFolder + L"modules\\MouseUtils\\MouseJumpUI\\"; + registryPreviewDir = installationFolder + L"modules\\RegistryPreview\\"; peekDir = installationFolder + L"modules\\Peek\\"; try @@ -1295,6 +1319,8 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) DeleteFile((fileExplorerAddOnsDir + file).c_str()); DeleteFile((hostsDir + file).c_str()); DeleteFile((fileLocksmithDir + file).c_str()); + DeleteFile((mouseJumpDir + file).c_str()); + DeleteFile((registryPreviewDir + file).c_str()); DeleteFile((peekDir + file).c_str()); } @@ -1309,6 +1335,8 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall) DeleteFile((powerAccentDir + file).c_str()); DeleteFile((fileExplorerAddOnsDir + file).c_str()); DeleteFile((hostsDir + file).c_str()); + DeleteFile((mouseJumpDir + file).c_str()); + DeleteFile((registryPreviewDir + file).c_str()); } } catch (std::exception e) @@ -1342,17 +1370,19 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) } processes.resize(bytes / sizeof(processes[0])); - std::array processesToTerminate = { + std::array processesToTerminate = { L"PowerToys.PowerLauncher.exe", L"PowerToys.Settings.exe", L"PowerToys.Awake.exe", L"PowerToys.FancyZones.exe", L"PowerToys.FancyZonesEditor.exe", L"PowerToys.FileLocksmithUI.exe", + L"PowerToys.MouseJumpUI.exe", L"PowerToys.ColorPickerUI.exe", L"PowerToys.AlwaysOnTop.exe", + L"PowerToys.exe", + L"PowerToys.RegistryPreview.exe", L"PowerToys.Peek.UI.exe", - L"PowerToys.exe" }; for (const auto procID : processes) @@ -1407,6 +1437,20 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall) return WcaFinalize(er); } +void initSystemLogger() +{ + static std::once_flag initLoggerFlag; + std::call_once(initLoggerFlag, []() { + WCHAR temp_path[MAX_PATH]; + auto ret = GetTempPath(MAX_PATH, temp_path); + + if (ret) + { + Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L""); + } + }); +} + // DllMain - Initialize and cleanup WiX custom action utils. extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID) { @@ -1414,6 +1458,7 @@ extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in L { case DLL_PROCESS_ATTACH: WcaGlobalInitialize(hInst); + initSystemLogger(); TraceLoggingRegister(g_hProvider); DLL_HANDLE = hInst; break; diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def index 27d59b2fda..21f2547052 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.def +++ b/installer/PowerToysSetupCustomActions/CustomAction.def @@ -1,6 +1,7 @@ LIBRARY "PowerToysSetupCustomActions" EXPORTS + CheckGPOCA ApplyModulesRegistryChangeSetsCA CreateScheduledTaskCA CreateWinAppSDKHardlinksCA diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index 07b2a3a065..f7370d499d 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -1,4 +1,4 @@ - + @@ -33,8 +33,10 @@ - $(Platform)\$(Configuration)\ - $(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\obj\ + $(Platform)\$(Configuration)\MachineSetup + $(Platform)\$(Configuration)\UserSetup + $(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\MachineSetup\obj\ + $(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\UserSetup\obj\ true @@ -47,8 +49,33 @@ call cmd /C "copy ""$(ProjectDir)DepsFilesLists.h"" ""$(ProjectDir)DepsFilesLists.h.bk""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\AlwaysOnTop.wxs"" ""$(ProjectDir)..\PowerToysSetup\AlwaysOnTop.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Awake.wxs"" ""$(ProjectDir)..\PowerToysSetup\Awake.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs"" ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Core.wxs"" ""$(ProjectDir)..\PowerToysSetup\Core.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\FancyZones.wxs"" ""$(ProjectDir)..\PowerToysSetup\FancyZones.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\FileExplorerPreview.wxs"" ""$(ProjectDir)..\PowerToysSetup\FileExplorerPreview.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\FileLocksmith.wxs"" ""$(ProjectDir)..\PowerToysSetup\FileLocksmith.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Hosts.wxs"" ""$(ProjectDir)..\PowerToysSetup\Hosts.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ImageResizer.wxs"" ""$(ProjectDir)..\PowerToysSetup\ImageResizer.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\KeyboardManager.wxs"" ""$(ProjectDir)..\PowerToysSetup\KeyboardManager.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\MeasureTool.wxs"" ""$(ProjectDir)..\PowerToysSetup\MeasureTool.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\MouseUtils.wxs"" ""$(ProjectDir)..\PowerToysSetup\MouseUtils.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\PowerAccent.wxs"" ""$(ProjectDir)..\PowerToysSetup\PowerAccent.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\PowerRename.wxs"" ""$(ProjectDir)..\PowerToysSetup\PowerRename.wxs.bk"""" call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Product.wxs"" ""$(ProjectDir)..\PowerToysSetup\Product.wxs.bk"""" - call powershell.exe -File parseRuntimes.ps1 -depsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Product.wxs" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\RegistryPreview.wxs"" ""$(ProjectDir)..\PowerToysSetup\RegistryPreview.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Resources.wxs"" ""$(ProjectDir)..\PowerToysSetup\Resources.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Run.wxs"" ""$(ProjectDir)..\PowerToysSetup\Run.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Settings.wxs"" ""$(ProjectDir)..\PowerToysSetup\Settings.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ShortcutGuide.wxs"" ""$(ProjectDir)..\PowerToysSetup\ShortcutGuide.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\TextExtractor.wxs"" ""$(ProjectDir)..\PowerToysSetup\TextExtractor.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Tools.wxs"" ""$(ProjectDir)..\PowerToysSetup\Tools.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\VideoConference.wxs"" ""$(ProjectDir)..\PowerToysSetup\VideoConference.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\WinAppSDK.wxs"" ""$(ProjectDir)..\PowerToysSetup\WinAppSDK.wxs.bk"""" + call powershell.exe -NonInteractive -executionpolicy Unrestricted -File parseRuntimes.ps1 -runtimedepsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\Settings\PowerToys.Settings.deps.json" -wpfdepsjsonpath "$(ProjectDir)..\..\$(Platform)\$(Configuration)\modules\ColorPicker\PowerToys.ColorPickerUI.deps.json" -depsfileslistspath "$(ProjectDir)DepsFilesLists.h" -productwxspath "$(ProjectDir)..\PowerToysSetup\Core.wxs" + if not "$(PerUser)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetup\generateAllFileComponents.ps1 -platform $(Platform) + if "$(PerUser)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetup\generateAllFileComponents.ps1 -platform $(Platform) -installscopeperuser $(PerUser) Backing up original files and populating .NET and WPF Runtime dependencies diff --git a/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 b/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 index f031c4429d..1ea58c268c 100644 --- a/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 +++ b/installer/PowerToysSetupCustomActions/parseRuntimes.ps1 @@ -1,13 +1,46 @@ [CmdletBinding()] Param( [Parameter(Mandatory = $True, Position = 1)] - [string]$depsjsonpath, + [string]$runtimedepsjsonpath, [Parameter(Mandatory = $True, Position = 2)] - [string]$depsfileslistspath, + [string]$wpfdepsjsonpath, [Parameter(Mandatory = $True, Position = 3)] + [string]$depsfileslistspath, + [Parameter(Mandatory = $True, Position = 4)] [string]$productwxspath ) +function Get-RuntimePack ($depsJsonFile, $runtimeName) { + Write-Host "Parsing $runtimeName Runtime" + $runtimePackList = ([array]$depsJsonFile.targets.PSObject.Properties)[-1].Value.PSObject.Properties | Where-Object { $_.Name -match "runtimepack.$runtimeName" }; + + if ($runtimePackList.Length -eq 0) { + Write-Host -ForegroundColor Red "$runtimeName has not been found" + exit 1 + } + + # Enumerate through array of custom objects and parse the names of the property values into a HashTable + $runtimePackList | ForEach-Object { + $runtimes += @{"$($_.Name -replace "runtimepack\.(\S+)\.\S+/\S+",'$1')" = $_.Value.PSObject.Properties.Value | ForEach-Object { + $_.PSObject.Properties.Name + } + } + } + Write-Output $runtimes; +} + +function Update-RuntimeHashTable () { + $runtimes = Get-RuntimePack $runtimeFile "Microsoft.NETCore.App.Runtime" + $runtimes = Get-RuntimePack $wpfRuntimeFile "Microsoft.WindowsDesktop.App.Runtime" + + # Find the dlls that exist in both the .NET Runtime and WPF Runtime deps list and filter out of WPF + $runtimeFileComparison = Compare-Object -ReferenceObject $runtimes["Microsoft.NETCore.App.Runtime"] -DifferenceObject $runtimes["Microsoft.WindowsDesktop.App.Runtime"] -IncludeEqual -ExcludeDifferent + + $runtimes["Microsoft.WindowsDesktop.App.Runtime"] = $runtimes["Microsoft.WindowsDesktop.App.Runtime"] | Where-Object { $_ -notin $runtimeFileComparison.InputObject } + + Write-Output $runtimes; +} + function Update-RuntimeFileList($runtimeToken, $runtimeKey) { $depsFilesLists -replace "($runtimeToken = )(.*);", "`$1 {`r`n$(($runtimes[$runtimeKey] | ForEach-Object {' L"'+$_+'"'} | Sort-Object) -join ",`r`n") };" } @@ -16,8 +49,7 @@ function Update-ProductWxsRuntimeFileList($runtimeToken, $runtimeKey) { $productWxs -replace "(define $runtimeToken=)(.*)?>", "`$1$($runtimes[$runtimeKey] -join ';')?>" } -function Update-DotnetFilesComponentGuid() -{ +function Update-DotnetFilesComponentGuid() { $productWxs -replace "Dlls_DotnetFiles_Component"" Guid=""([{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?)""", "Dlls_DotnetFiles_Component"" Guid=""$((New-Guid).ToString().ToUpper())""" } @@ -28,26 +60,12 @@ $depsFilesLists = Get-Content $depsfileslistspath; $productWxs = Get-Content $productwxspath; # Read the deps.json file and convert it to a JSON object -$runtimeFile = Get-Content $depsjsonpath | ConvertFrom-Json; +$runtimeFile = Get-Content $runtimedepsjsonpath | ConvertFrom-Json; +$wpfRuntimeFile = Get-Content $wpfdepsjsonpath | ConvertFrom-Json; $runtimes = @{} -Write-Host "Parsing .NET Runtimes from $depsjsonpath `r`n" - -$runtimeList = ([array]$runtimeFile.targets.PSObject.Properties)[-1].Value.PSObject.Properties | Where-Object { $_.Name -match "runtimepack" }; - -if ($runtimeList.Length -eq 0) { - Write-Host -ForegroundColor Red "No runtimes have been detected" - exit 1 -} - -# Enumerate through array of custom objects and parse the names of the property values into a HashTable -$runtimeList | ForEach-Object { - $runtimes += @{"$($_.Name -replace "runtimepack\.(\S+)\.\S+/\S+",'$1')" = $_.Value.PSObject.Properties.Value | ForEach-Object { - $_.PSObject.Properties.Name - } - } -} +$runtimes = Update-RuntimeHashTable Write-Host "Writing Microsoft.NETCore.App.Runtime files" $depsFilesLists = Update-RuntimeFileList "dotnetRuntimeFiles" "Microsoft.NETCore.App.Runtime" @@ -64,6 +82,4 @@ Write-Host "Updating $depsfileslistspath" Set-Content -Path $depsfileslistspath -Value $depsFilesLists Write-Host "Updating $productwxspath" -Set-Content -Path $productwxspath -Value $productWxs - - +Set-Content -Path $productwxspath -Value $productWxs \ No newline at end of file diff --git a/nuget.config b/nuget.config index 5a4b9c0fb9..81243afb3c 100644 --- a/nuget.config +++ b/nuget.config @@ -1,5 +1,13 @@ - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/Update/PowerToys.Update.cpp b/src/Update/PowerToys.Update.cpp index 31e4eb8143..d19647623d 100644 --- a/src/Update/PowerToys.Update.cpp +++ b/src/Update/PowerToys.Update.cpp @@ -110,7 +110,7 @@ bool InstallNewVersionStage1(fs::path installer) { // Detect if PT was running const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr); - const bool launch_powertoys = pt_main_window != nullptr; + if (pt_main_window != nullptr) { SendMessageW(pt_main_window, WM_CLOSE, 0, 0); @@ -119,10 +119,7 @@ bool InstallNewVersionStage1(fs::path installer) std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2 }; arguments += L" \""; arguments += installer.c_str(); - arguments += L"\" \""; - arguments += get_module_folderpath(); - arguments += L"\" "; - arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT : UPDATE_STAGE2_DONT_START_PT; + arguments += L"\""; SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC }; sei.lpFile = copy_in_temp->c_str(); @@ -137,7 +134,7 @@ bool InstallNewVersionStage1(fs::path installer) } } -bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view install_path, bool launch_powertoys) +bool InstallNewVersionStage2(std::wstring installer_path) { std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower); @@ -181,18 +178,6 @@ bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view inst state.state = UpdateState::upToDate; }); - 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; } @@ -230,7 +215,7 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) else if (action == UPDATE_NOW_LAUNCH_STAGE2) { using namespace std::string_view_literals; - const bool failed = !InstallNewVersionStage2(args[2], args[3], args[4] == std::wstring_view{ UPDATE_STAGE2_RESTART_PT }); + const bool failed = !InstallNewVersionStage2(args[2]); if (failed) { UpdateState::store([&](UpdateState& state) { diff --git a/src/common/AllExperiments/AllExperiments.csproj b/src/common/AllExperiments/AllExperiments.csproj new file mode 100644 index 0000000000..44b823cbbc --- /dev/null +++ b/src/common/AllExperiments/AllExperiments.csproj @@ -0,0 +1,27 @@ + + + + net7.0-windows10.0.19041.0 + enable + enable + PowerToys.AllExperiments + .\Microsoft.VariantAssignment\ + + + + + + + + + + + + + + + + + + + diff --git a/src/common/AllExperiments/Experiments.cs b/src/common/AllExperiments/Experiments.cs new file mode 100644 index 0000000000..38ac694637 --- /dev/null +++ b/src/common/AllExperiments/Experiments.cs @@ -0,0 +1,212 @@ +// 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. + +using System.Globalization; +using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; +using Microsoft.PowerToys.Telemetry; +using Microsoft.VariantAssignment.Client; +using Microsoft.VariantAssignment.Contract; +using Windows.System.Profile; + +namespace AllExperiments +{ + // The dependencies required to build this project are only available in the official build pipeline and are internal to Microsoft. + // However, this project is not required to build a test version of the application. + public class Experiments + { + public enum ExperimentState + { + Enabled, + Disabled, + NotLoaded, + } + +#pragma warning disable SA1401 // Need to use LandingPageExperiment as a static property in OobeShellPage.xaml.cs +#pragma warning disable CA2211 // Non-constant fields should not be visible + public static ExperimentState LandingPageExperiment = ExperimentState.NotLoaded; +#pragma warning restore CA2211 +#pragma warning restore SA1401 + + public async Task EnableLandingPageExperimentAsync() + { + if (Experiments.LandingPageExperiment != ExperimentState.NotLoaded) + { + return Experiments.LandingPageExperiment == ExperimentState.Enabled; + } + + Experiments varServ = new Experiments(); + await varServ.VariantAssignmentProvider_Initialize(); + var landingPageExperiment = varServ.IsExperiment; + + Experiments.LandingPageExperiment = landingPageExperiment ? ExperimentState.Enabled : ExperimentState.Disabled; + + return landingPageExperiment; + } + + private async Task VariantAssignmentProvider_Initialize() + { + IsExperiment = false; + string jsonFilePath = CreateFilePath(); + + var vaSettings = new VariantAssignmentClientSettings + { + Endpoint = new Uri("https://default.exp-tas.com/exptas77/a7a397e7-6fbe-4f21-a4e9-3f542e4b000e-exppowertoys/api/v1/tas"), + EnableCaching = true, + ResponseCacheTime = TimeSpan.FromMinutes(5), + }; + + try + { + var vaClient = vaSettings.GetTreatmentAssignmentServiceClient(); + var vaRequest = GetVariantAssignmentRequest(); + using var variantAssignments = await vaClient.GetVariantAssignmentsAsync(vaRequest).ConfigureAwait(false); + + if (variantAssignments.AssignedVariants.Count != 0) + { + var dataVersion = variantAssignments.DataVersion; + var featureVariables = variantAssignments.GetFeatureVariables(); + var assignmentContext = variantAssignments.GetAssignmentContext(); + var featureFlagValue = featureVariables[0].GetStringValue(); + + var experimentGroup = string.Empty; + string json = File.ReadAllText(jsonFilePath); + var jsonDictionary = JsonSerializer.Deserialize>(json); + + if (jsonDictionary != null) + { + if (!jsonDictionary.ContainsKey("dataversion")) + { + jsonDictionary.Add("dataversion", dataVersion); + } + + if (!jsonDictionary.ContainsKey("variantassignment")) + { + jsonDictionary.Add("variantassignment", featureFlagValue); + } + else + { + var jsonDataVersion = jsonDictionary["dataversion"].ToString(); + if (jsonDataVersion != null && int.Parse(jsonDataVersion, CultureInfo.InvariantCulture) < dataVersion) + { + jsonDictionary["dataversion"] = dataVersion; + jsonDictionary["variantassignment"] = featureFlagValue; + } + } + + experimentGroup = jsonDictionary["variantassignment"].ToString(); + + string output = JsonSerializer.Serialize(jsonDictionary); + File.WriteAllText(jsonFilePath, output); + } + + if (experimentGroup == "alternate" && AssignmentUnit != string.Empty) + { + IsExperiment = true; + } + + PowerToysTelemetry.Log.WriteEvent(new OobeVariantAssignmentEvent() { AssignmentContext = assignmentContext, ClientID = AssignmentUnit }); + } + } + catch (HttpRequestException ex) + { + string json = File.ReadAllText(jsonFilePath); + var jsonDictionary = JsonSerializer.Deserialize>(json); + + if (jsonDictionary != null) + { + if (jsonDictionary.ContainsKey("variantassignment")) + { + if (jsonDictionary["variantassignment"].ToString() == "alternate" && AssignmentUnit != string.Empty) + { + IsExperiment = true; + } + } + else + { + jsonDictionary["variantassignment"] = "current"; + } + } + + string output = JsonSerializer.Serialize(jsonDictionary); + File.WriteAllText(jsonFilePath, output); + + Logger.LogError("Error getting to TAS endpoint", ex); + } + catch (Exception ex) + { + Logger.LogError("Error getting variant assignments for experiment", ex); + } + } + + public bool IsExperiment { get; set; } + + private string? AssignmentUnit { get; set; } + + private IVariantAssignmentRequest GetVariantAssignmentRequest() + { + var jsonFilePath = CreateFilePath(); + try + { + if (!File.Exists(jsonFilePath)) + { + AssignmentUnit = Guid.NewGuid().ToString(); + var data = new Dictionary() + { + ["clientid"] = AssignmentUnit, + }; + string jsonData = JsonSerializer.Serialize(data); + File.WriteAllText(jsonFilePath, jsonData); + } + else + { + string json = File.ReadAllText(jsonFilePath); + var jsonDictionary = System.Text.Json.JsonSerializer.Deserialize>(json); + if (jsonDictionary != null) + { + AssignmentUnit = jsonDictionary["clientid"]?.ToString(); + } + } + } + catch (Exception ex) + { + Logger.LogError("Error creating/getting AssignmentUnit", ex); + } + + var attrNames = new List { "FlightRing", "c:InstallLanguage" }; + var attrData = AnalyticsInfo.GetSystemPropertiesAsync(attrNames).AsTask().GetAwaiter().GetResult(); + + var flightRing = string.Empty; + var installLanguage = string.Empty; + + if (attrData.ContainsKey("FlightRing")) + { + flightRing = attrData["FlightRing"]; + } + + if (attrData.ContainsKey("InstallLanguage")) + { + installLanguage = attrData["InstallLanguage"]; + } + + return new VariantAssignmentRequest + { + Parameters = + { + { "installLanguage", installLanguage }, + { "flightRing", flightRing }, + { "clientid", AssignmentUnit }, + }, + }; + } + + private string CreateFilePath() + { + var exeDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var settingsPath = @"Microsoft\PowerToys\experimentation.json"; + var filePath = Path.Combine(exeDir, settingsPath); + return filePath; + } + } +} diff --git a/src/settings-ui/Settings.UI.Library/Utilities/Logger.cs b/src/common/AllExperiments/Logger.cs similarity index 93% rename from src/settings-ui/Settings.UI.Library/Utilities/Logger.cs rename to src/common/AllExperiments/Logger.cs index fbac2c88fb..7604618bdf 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/Logger.cs +++ b/src/common/AllExperiments/Logger.cs @@ -7,7 +7,7 @@ using System.Diagnostics; using System.Globalization; using System.IO.Abstractions; -namespace Microsoft.PowerToys.Settings.UI.Library.Utilities +namespace AllExperiments { public static class Logger { @@ -15,7 +15,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities private static readonly IPath Path = FileSystem.Path; private static readonly IDirectory Directory = FileSystem.Directory; - private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs"); + private static readonly string ApplicationLogPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\Settings Logs\\Experimentation"); static Logger() { @@ -74,7 +74,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities StackTrace stackTrace = new StackTrace(); var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; + var className = methodName?.DeclaringType?.Name; return "[Method]: " + methodName?.Name + " [Class]: " + className; } } diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs new file mode 100644 index 0000000000..ee08acd718 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentClientExtensionMethods.cs @@ -0,0 +1,21 @@ +// 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. + +using Microsoft.VariantAssignment.Contract; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Client +{ +#pragma warning disable SA1200 // Using directives should be placed correctly + using TreatmentAssignmentServiceClient = VariantAssignmentServiceClient; +#pragma warning restore SA1200 // Using directives should be placed correctly + + public static class VariantAssignmentClientExtensionMethods + { + public static IVariantAssignmentProvider GetTreatmentAssignmentServiceClient(this VariantAssignmentClientSettings settings) + { + return new TreatmentAssignmentServiceClient(); + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs new file mode 100644 index 0000000000..373651f83a --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Client/VariantAssignmentServiceClient.cs @@ -0,0 +1,23 @@ +// 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. + +using Microsoft.VariantAssignment.Contract; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Client +{ + internal sealed partial class VariantAssignmentServiceClient : IVariantAssignmentProvider, IDisposable + where TServerResponse : VariantAssignmentServiceResponse + { + public void Dispose() + { + throw new NotImplementedException(); + } + + public Task GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default) + { + return Task.FromResult(EmptyVariantAssignmentResponse.Instance); + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs new file mode 100644 index 0000000000..0e0cd54094 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/EmptyVariantAssignmentResponse.cs @@ -0,0 +1,54 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public class EmptyVariantAssignmentResponse : IVariantAssignmentResponse + { + /// + /// Singleton instance of . + /// + public static readonly IVariantAssignmentResponse Instance = new EmptyVariantAssignmentResponse(); + + public EmptyVariantAssignmentResponse() + { + } + + public long DataVersion => 0; + + public string Thumbprint => string.Empty; + + /// + public IReadOnlyCollection AssignedVariants => Array.Empty(); + + /// +#pragma warning disable CS8603 // Possible null reference return. + public IFeatureVariable GetFeatureVariable(IReadOnlyList path) => null; +#pragma warning restore CS8603 // Possible null reference return. + + /// + public IReadOnlyList GetFeatureVariables(IReadOnlyList prefix) => Array.Empty(); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } + + string IVariantAssignmentResponse.GetAssignmentContext() + { + throw new NotImplementedException(); + } + + IReadOnlyList IVariantAssignmentResponse.GetFeatureVariables() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs new file mode 100644 index 0000000000..6c9a31e8ce --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IAssignedVariant.cs @@ -0,0 +1,11 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IAssignedVariant + { + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs new file mode 100644 index 0000000000..fc9193ed0d --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IFeatureVariable.cs @@ -0,0 +1,16 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IFeatureVariable + { + /// + /// Gets the variable's value as a string. + /// + /// String value of the variable. + string GetStringValue(); + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs new file mode 100644 index 0000000000..dad9d39038 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentProvider.cs @@ -0,0 +1,18 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IVariantAssignmentProvider : IDisposable + { + /// + /// Computes variant assignments based on data. + /// + /// Variant assignment parameters. + /// Propagates notification that operations should be canceled. + /// An awaitable task that returns a . + Task GetVariantAssignmentsAsync(IVariantAssignmentRequest request, CancellationToken ct = default); + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs new file mode 100644 index 0000000000..9639a3a58d --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentRequest.cs @@ -0,0 +1,15 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public interface IVariantAssignmentRequest + { + /// + /// Gets inputs used for evaluating filters, assignment units, etc. + /// + IReadOnlyCollection<(string Key, string Value)> Parameters { get; } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs new file mode 100644 index 0000000000..29ee2209de --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/IVariantAssignmentResponse.cs @@ -0,0 +1,48 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + /// + /// Snapshot of variant assignments. + /// + public interface IVariantAssignmentResponse : IDisposable + { + ///// + ///// Gets the serial number of variant assignment configuration snapshot used when assigning variants. + ///// + long DataVersion { get; } + + ///// + ///// Get a hash of the response suitable for caching. + ///// + // string Thumbprint { get; } + + /// + /// Gets the variants assigned based on request parameters and a variant configuration snapshot. + /// + IReadOnlyCollection AssignedVariants { get; } + + /// + /// Gets feature variables assigned by variants in this response. + /// + /// (Optional) Filter feature variables where contains the . + /// Range of matching feature variables. + IReadOnlyList GetFeatureVariables(IReadOnlyList prefix); + + // this actually part of the interface but gets the job done + IReadOnlyList GetFeatureVariables(); + + // this actually part of the interface but gets the job done + string GetAssignmentContext(); + + /// + /// Gets a single feature variable assigned by variants in this response. + /// + /// Exact feature variable path. + /// Matching feature variable or null. + IFeatureVariable GetFeatureVariable(IReadOnlyList path); + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs new file mode 100644 index 0000000000..6db91f6ffd --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/TreatmentAssignmentServiceResponse.cs @@ -0,0 +1,11 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + internal sealed class TreatmentAssignmentServiceResponse : VariantAssignmentServiceResponse + { + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs new file mode 100644 index 0000000000..1ea295bfda --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentClientSettings.cs @@ -0,0 +1,31 @@ +// 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. + +using System.ComponentModel.DataAnnotations; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + /// + /// Configuration for variant assignment service client. + /// + public class VariantAssignmentClientSettings + { + /// + /// Gets or sets the variant assignment service endpoint URL. + /// + [Required] + public Uri? Endpoint { get; set; } + + /// + /// Gets or sets a value indicating whether gets or sets a value whether client side request caching should be enabled. + /// + public bool EnableCaching { get; set; } + + /// + /// Gets or sets the the maximum time a cached variant assignment response may be used without re-validating. + /// + public TimeSpan ResponseCacheTime { get; set; } + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs new file mode 100644 index 0000000000..976ce53531 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentRequest.cs @@ -0,0 +1,21 @@ +// 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. + +using System.Collections.Specialized; + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + public class VariantAssignmentRequest : IVariantAssignmentRequest + { + private NameValueCollection _parameters = new NameValueCollection(); + + /// + /// Gets or sets mutable . + /// + public NameValueCollection Parameters { get => _parameters; set => _parameters = value; } + + IReadOnlyCollection<(string Key, string Value)> IVariantAssignmentRequest.Parameters => (IReadOnlyCollection<(string Key, string Value)>)_parameters; + } +} diff --git a/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs new file mode 100644 index 0000000000..e87425f4d3 --- /dev/null +++ b/src/common/AllExperiments/Microsoft.VariantAssignment/Contract/VariantAssignmentServiceResponse.cs @@ -0,0 +1,48 @@ +// 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. + +// The goal of this class is to just mock out the Microsoft.VariantAssignment close source objects +namespace Microsoft.VariantAssignment.Contract +{ + /// + /// Mutable implementation of for (de)serialization. + /// + internal class VariantAssignmentServiceResponse : IVariantAssignmentResponse, IDisposable + { + /// + public virtual long DataVersion { get; set; } + + public virtual IReadOnlyCollection AssignedVariants { get; set; } = Array.Empty(); + + public IFeatureVariable GetFeatureVariable(IReadOnlyList path) + { + throw new NotImplementedException(); + } + + public IReadOnlyList GetFeatureVariables(IReadOnlyList prefix) + { + throw new NotImplementedException(); + } + + protected virtual void Dispose(bool disposing) + { + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public IReadOnlyList GetFeatureVariables() + { + throw new NotImplementedException(); + } + + public string GetAssignmentContext() + { + return string.Empty; + } + } +} diff --git a/src/common/Common.UI/Common.UI.csproj b/src/common/Common.UI/Common.UI.csproj index 9175a0706c..a4475f640c 100644 --- a/src/common/Common.UI/Common.UI.csproj +++ b/src/common/Common.UI/Common.UI.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/common/Common.UI/SettingsDeepLink.cs b/src/common/Common.UI/SettingsDeepLink.cs index 42da58b8a0..c63f3fc1ad 100644 --- a/src/common/Common.UI/SettingsDeepLink.cs +++ b/src/common/Common.UI/SettingsDeepLink.cs @@ -24,6 +24,7 @@ namespace Common.UI ShortcutGuide, VideoConference, Hosts, + RegistryPreview, } private static string SettingsWindowNameToString(SettingsWindow value) @@ -56,6 +57,8 @@ namespace Common.UI return "VideoConference"; case SettingsWindow.Hosts: return "Hosts"; + case SettingsWindow.RegistryPreview: + return "RegistryPreview"; default: { return string.Empty; diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 513ba8a5c1..9e4a6ef347 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -6,110 +6,130 @@ namespace winrt::PowerToys::GPOWrapper::implementation { GpoRuleConfigured GPOWrapper::GetConfiguredAlwaysOnTopEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredAlwaysOnTopEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredAlwaysOnTopEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredAwakeEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredAwakeEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredAwakeEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredColorPickerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredColorPickerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredColorPickerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredFancyZonesEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredFancyZonesEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredFileLocksmithEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredFileLocksmithEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredFileLocksmithEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredSvgPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredSvgPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredSvgPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMarkdownPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMarkdownPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMarkdownPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMonacoPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMonacoPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMonacoPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPdfPreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPdfPreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPdfPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredGcodePreviewEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredGcodePreviewEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredGcodePreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredSvgThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredSvgThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPdfThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPdfThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPdfThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredGcodeThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredGcodeThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredStlThumbnailsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredStlThumbnailsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredStlThumbnailsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredHostsFileEditorEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredHostsFileEditorEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredHostsFileEditorEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredImageResizerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredImageResizerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredImageResizerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredKeyboardManagerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredKeyboardManagerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredKeyboardManagerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredFindMyMouseEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredFindMyMouseEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredFindMyMouseEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMouseHighlighterEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMouseHighlighterEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMouseHighlighterEnabledValue()); + } + GpoRuleConfigured GPOWrapper::GetConfiguredMouseJumpEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredMouseJumpEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredMousePointerCrosshairsEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredMousePointerCrosshairsEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredMousePointerCrosshairsEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPowerRenameEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPowerRenameEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPowerRenameEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredPowerLauncherEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredPowerLauncherEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredPowerLauncherEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredQuickAccentEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredQuickAccentEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredQuickAccentEnabledValue()); + } + GpoRuleConfigured GPOWrapper::GetConfiguredRegistryPreviewEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredRegistryPreviewEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredScreenRulerEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredScreenRulerEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredScreenRulerEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredShortcutGuideEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredShortcutGuideEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredShortcutGuideEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredTextExtractorEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredTextExtractorEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredTextExtractorEnabledValue()); + } + GpoRuleConfigured GPOWrapper::GetConfiguredPastePlainEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredPastePlainEnabledValue()); } GpoRuleConfigured GPOWrapper::GetConfiguredVideoConferenceMuteEnabledValue() { - return (GpoRuleConfigured)powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue(); + return static_cast(powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue()); + } + GpoRuleConfigured GPOWrapper::GetDisableAutomaticUpdateDownloadValue() + { + return static_cast(powertoys_gpo::getDisableAutomaticUpdateDownloadValue()); + } + GpoRuleConfigured GPOWrapper::GetAllowExperimentationValue() + { + return static_cast(powertoys_gpo::getAllowExperimentationValue()); } } diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index 25b8d87445..77d6ba72f9 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -26,14 +26,19 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredKeyboardManagerEnabledValue(); static GpoRuleConfigured GetConfiguredFindMyMouseEnabledValue(); static GpoRuleConfigured GetConfiguredMouseHighlighterEnabledValue(); + static GpoRuleConfigured GetConfiguredMouseJumpEnabledValue(); static GpoRuleConfigured GetConfiguredMousePointerCrosshairsEnabledValue(); static GpoRuleConfigured GetConfiguredPowerRenameEnabledValue(); static GpoRuleConfigured GetConfiguredPowerLauncherEnabledValue(); static GpoRuleConfigured GetConfiguredQuickAccentEnabledValue(); + static GpoRuleConfigured GetConfiguredRegistryPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue(); static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); + static GpoRuleConfigured GetConfiguredPastePlainEnabledValue(); static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue(); + static GpoRuleConfigured GetDisableAutomaticUpdateDownloadValue(); + static GpoRuleConfigured GetAllowExperimentationValue(); }; } diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index f316b63fe1..b0f1633dfc 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -30,14 +30,19 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredKeyboardManagerEnabledValue(); static GpoRuleConfigured GetConfiguredFindMyMouseEnabledValue(); static GpoRuleConfigured GetConfiguredMouseHighlighterEnabledValue(); + static GpoRuleConfigured GetConfiguredMouseJumpEnabledValue(); static GpoRuleConfigured GetConfiguredMousePointerCrosshairsEnabledValue(); static GpoRuleConfigured GetConfiguredPowerRenameEnabledValue(); static GpoRuleConfigured GetConfiguredPowerLauncherEnabledValue(); static GpoRuleConfigured GetConfiguredQuickAccentEnabledValue(); + static GpoRuleConfigured GetConfiguredRegistryPreviewEnabledValue(); static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue(); static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue(); static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue(); + static GpoRuleConfigured GetConfiguredPastePlainEnabledValue(); static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue(); + static GpoRuleConfigured GetDisableAutomaticUpdateDownloadValue(); + static GpoRuleConfigured GetAllowExperimentationValue(); } } } diff --git a/src/common/GPOWrapperProjection/GPOWrapper.cs b/src/common/GPOWrapperProjection/GPOWrapper.cs index 7017a42084..108907d143 100644 --- a/src/common/GPOWrapperProjection/GPOWrapper.cs +++ b/src/common/GPOWrapperProjection/GPOWrapper.cs @@ -41,5 +41,10 @@ namespace PowerToys.GPOWrapperProjection { return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredTextExtractorEnabledValue(); } + + public static GpoRuleConfigured GetConfiguredPastePlainEnabledValue() + { + return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredPastePlainEnabledValue(); + } } } diff --git a/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj b/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj index bd20c2a527..4cfc2521c6 100644 --- a/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj +++ b/src/common/GPOWrapperProjection/GPOWrapperProjection.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Logs/Logger.cs b/src/common/ManagedCommon/Logger.cs similarity index 68% rename from src/modules/fancyzones/editor/FancyZonesEditor/Logs/Logger.cs rename to src/common/ManagedCommon/Logger.cs index 1998ddd516..0d6cc32f2d 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Logs/Logger.cs +++ b/src/common/ManagedCommon/Logger.cs @@ -8,16 +8,16 @@ using System.Globalization; using System.IO; using System.IO.Abstractions; using System.Reflection; +using System.Runtime.Serialization; using interop; -namespace FancyZonesEditor.Logs +namespace ManagedCommon { public static class Logger { private static readonly IFileSystem _fileSystem = new FileSystem(); private static readonly Assembly Assembly = Assembly.GetExecutingAssembly(); - public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion; - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "FancyZones\\Editor\\Logs\\", Version); + private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion; private static readonly string Error = "Error"; private static readonly string Warning = "Warning"; @@ -25,15 +25,29 @@ namespace FancyZonesEditor.Logs private static readonly string Debug = "Debug"; private static readonly string TraceFlag = "Trace"; - static Logger() + /// + /// Initializes the logger and sets the path for logging. + /// + /// InitializeLogger("\\FancyZones\\Editor\\Logs") + /// The path to the log files folder. + /// If the process using Logger is a low-privilege process. + public static void InitializeLogger(string applicationLogPath, bool isLocalLow = false) { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) + if (isLocalLow) { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); + applicationLogPath = Environment.GetEnvironmentVariable("userprofile") + "\\appdata\\LocalLow\\Microsoft\\PowerToys" + applicationLogPath + "\\" + Version; + } + else + { + applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version; } - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); + if (!_fileSystem.Directory.Exists(applicationLogPath)) + { + _fileSystem.Directory.CreateDirectory(applicationLogPath); + } + + var logFilePath = _fileSystem.Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); @@ -91,7 +105,7 @@ namespace FancyZonesEditor.Logs private static string GetCallerInfo() { - StackTrace stackTrace = new StackTrace(); + StackTrace stackTrace = new(); var methodName = stackTrace.GetFrame(3)?.GetMethod(); var className = methodName?.DeclaringType.Name; diff --git a/src/common/ManagedCommon/ManagedCommon.csproj b/src/common/ManagedCommon/ManagedCommon.csproj index 18608ef4f8..05f9bd34e1 100644 --- a/src/common/ManagedCommon/ManagedCommon.csproj +++ b/src/common/ManagedCommon/ManagedCommon.csproj @@ -13,10 +13,12 @@ - + + + diff --git a/src/common/SettingsAPI/settings_objects.cpp b/src/common/SettingsAPI/settings_objects.cpp index f7bd573845..6f3aa4c792 100644 --- a/src/common/SettingsAPI/settings_objects.cpp +++ b/src/common/SettingsAPI/settings_objects.cpp @@ -247,7 +247,7 @@ namespace PowerToysSettings bool Settings::serialize_to_buffer(wchar_t* buffer, int* buffer_size) { auto result = m_json.Stringify(); - const int result_len = (int)result.size() + 1; + const int result_len = static_cast(result.size() + 1); if (buffer == nullptr || *buffer_size < result_len) { diff --git a/src/common/SettingsAPI/settings_objects.h b/src/common/SettingsAPI/settings_objects.h index cee95d7a56..1c84ac19b7 100644 --- a/src/common/SettingsAPI/settings_objects.h +++ b/src/common/SettingsAPI/settings_objects.h @@ -220,7 +220,7 @@ namespace PowerToysSettings std::array key_states{}; // Zero-initialize std::array output; const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) - auto output_bytes = ToUnicodeEx(key_code, scan_code, key_states.data(), output.data(), (int)output.size() - 1, wFlags, layout); + auto output_bytes = ToUnicodeEx(key_code, scan_code, key_states.data(), output.data(), static_cast(output.size()) - 1, wFlags, layout); if (output_bytes <= 0) { // If ToUnicodeEx fails (e.g. for F1-F12 keys) use GetKeyNameTextW diff --git a/src/common/Themes/icon_helpers.cpp b/src/common/Themes/icon_helpers.cpp index e5387e85a9..c426a0c747 100644 --- a/src/common/Themes/icon_helpers.cpp +++ b/src/common/Themes/icon_helpers.cpp @@ -14,7 +14,7 @@ HRESULT GetIconIndexFromPath(_In_ PCWSTR path, _Out_ int* index) if (!PathIsRelative(path)) { DWORD attrib = GetFileAttributes(path); - HIMAGELIST himl = (HIMAGELIST)SHGetFileInfo(path, attrib, &shFileInfo, sizeof(shFileInfo), (SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES)); + auto himl =SHGetFileInfo(path, attrib, &shFileInfo, sizeof(shFileInfo), (SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES)); if (himl) { *index = shFileInfo.iIcon; @@ -61,14 +61,14 @@ HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UIN if (hBitmap != NULL) { // Select bitmap into DC - HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap); + HBITMAP hBitmapOld = static_cast(SelectObject(hDC, hBitmap)); if (hBitmapOld != NULL) { // Draw icon into DC if (DrawIconEx(hDC, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL)) { // Restore original bitmap in DC - hBitmapResult = (HBITMAP)SelectObject(hDC, hBitmapOld); + hBitmapResult = static_cast(SelectObject(hDC, hBitmapOld)); hBitmapOld = NULL; hBitmap = NULL; } diff --git a/src/common/Themes/theme_helpers.cpp b/src/common/Themes/theme_helpers.cpp index 280eda9ded..c00bd92f76 100644 --- a/src/common/Themes/theme_helpers.cpp +++ b/src/common/Themes/theme_helpers.cpp @@ -28,10 +28,7 @@ AppTheme ThemeHelpers::GetAppTheme() } // convert bytes written to our buffer to an int, assuming little-endian - auto i = int(buffer[3] << 24 | - buffer[2] << 16 | - buffer[1] << 8 | - buffer[0]); + auto i = static_cast(buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]); return AppTheme(i); } diff --git a/src/common/Themes/theme_listener.cpp b/src/common/Themes/theme_listener.cpp index fb81a42e4c..3a31b0db3c 100644 --- a/src/common/Themes/theme_listener.cpp +++ b/src/common/Themes/theme_listener.cpp @@ -8,7 +8,7 @@ #pragma warning(disable : 4702) DWORD WINAPI _checkTheme(LPVOID lpParam) { - auto listener = (ThemeListener*)lpParam; + auto listener = static_cast(lpParam); listener->CheckTheme(); return 0; } diff --git a/src/common/Themes/windows_colors.cpp b/src/common/Themes/windows_colors.cpp index a30329c181..e5a8eec732 100644 --- a/src/common/Themes/windows_colors.cpp +++ b/src/common/Themes/windows_colors.cpp @@ -11,7 +11,7 @@ DWORD WindowsColors::rgb_color(DWORD abgr_color) } DWORD WindowsColors::rgb_color(winrt::Windows::UI::Color color) { - return ((DWORD)color.R << 16) | ((DWORD)color.G << 8) | ((DWORD)color.B); + return static_cast((color.R << 16) | (color.G << 8) | (color.B)); } WindowsColors::Color WindowsColors::get_button_face_color() { diff --git a/src/common/interop/PowerToys.Interop.vcxproj b/src/common/interop/PowerToys.Interop.vcxproj index 9a88fcbb00..7f2f3f7da0 100644 --- a/src/common/interop/PowerToys.Interop.vcxproj +++ b/src/common/interop/PowerToys.Interop.vcxproj @@ -3,8 +3,6 @@ PowerToys.Interop - Microsoft Corp. - Copyright (C) 2022 Microsoft Corp. diff --git a/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj b/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj index 4efdbffc04..a89ba3f7ec 100644 --- a/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj +++ b/src/common/interop/interop-tests/Microsoft.Interop.Tests.csproj @@ -47,9 +47,9 @@ - - - + + + diff --git a/src/common/interop/interop.cpp b/src/common/interop/interop.cpp index b64e577c07..c02ad5de98 100644 --- a/src/common/interop/interop.cpp +++ b/src/common/interop/interop.cpp @@ -187,6 +187,10 @@ public return gcnew String(CommonSharedConstants::FZE_EXIT_EVENT); } + static String ^ FZEToggleEvent() { + return gcnew String(CommonSharedConstants::FANCY_ZONES_EDITOR_TOGGLE_EVENT); + } + static String ^ ColorPickerSendSettingsTelemetryEvent() { return gcnew String(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT); } @@ -211,6 +215,18 @@ public return gcnew String(CommonSharedConstants::POWERACCENT_EXIT_EVENT); } + static String ^ ShortcutGuideTriggerEvent() { + return gcnew String(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT); + } + + static String ^ RegistryPreviewTriggerEvent() { + return gcnew String(CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT); + } + + static String ^ MeasureToolTriggerEvent() { + return gcnew String(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT); + } + static String ^ GcodePreviewResizeEvent() { return gcnew String(CommonSharedConstants::GCODE_PREVIEW_RESIZE_EVENT); } diff --git a/src/common/interop/keyboard_layout.cpp b/src/common/interop/keyboard_layout.cpp index 441059bbb0..87562d4bf3 100644 --- a/src/common/interop/keyboard_layout.cpp +++ b/src/common/interop/keyboard_layout.cpp @@ -56,7 +56,7 @@ bool mapKeycodeToUnicode(const int vCode, HKL layout, const BYTE* keyState, std: const UINT scanCode = MapVirtualKeyExW(vCode, MAPVK_VK_TO_VSC, layout); // Get the unicode representation from the virtual key code and scan code pair const UINT wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer) - const int result = ToUnicodeEx(vCode, scanCode, keyState, outBuffer.data(), (int)outBuffer.size(), wFlags, layout); + const int result = ToUnicodeEx(vCode, scanCode, keyState, outBuffer.data(), static_cast(outBuffer.size()), wFlags, layout); return result != 0; } diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 069bc10a79..69a69e7f28 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -28,6 +28,8 @@ namespace CommonSharedConstants // Path to the event used to show Color Picker const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525"; + const wchar_t SHORTCUT_GUIDE_TRIGGER_EVENT[] = L"Local\\ShortcutGuide-TriggerEvent-d4275ad3-2531-4d19-9252-c0becbd9b496"; + const wchar_t SHORTCUT_GUIDE_EXIT_EVENT[] = L"Local\\ShortcutGuide-ExitEvent-35697cdd-a3d2-47d6-a246-34efcc73eac0"; const wchar_t FANCY_ZONES_EDITOR_TOGGLE_EVENT[] = L"Local\\FancyZones-ToggleEditorEvent-1e174338-06a3-472b-874d-073b21c62f14"; @@ -44,6 +46,12 @@ namespace CommonSharedConstants // Path to the event used by PowerOCR const wchar_t SHOW_POWEROCR_SHARED_EVENT[] = L"Local\\PowerOCREvent-dc864e06-e1af-4ecc-9078-f98bee745e3a"; + // Path to the event used by RegistryPreview + const wchar_t REGISTRY_PREVIEW_TRIGGER_EVENT[] = L"Local\\RegistryPreviewEvent-4C559468-F75A-4E7F-BC4F-9C9688316687"; + + // Path to the event used by MeasureTool + const wchar_t MEASURE_TOOL_TRIGGER_EVENT[] = L"Local\\MeasureToolEvent-3d46745f-09b3-4671-a577-236be7abd199"; + // Path to the event used by GcodePreviewHandler const wchar_t GCODE_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysGcodePreviewResizeEvent-6ff1f9bd-ccbd-4b24-a79f-40a34fb0317d"; diff --git a/src/common/interop/two_way_pipe_message_ipc.cpp b/src/common/interop/two_way_pipe_message_ipc.cpp index 69215665c7..7dd1dedf36 100644 --- a/src/common/interop/two_way_pipe_message_ipc.cpp +++ b/src/common/interop/two_way_pipe_message_ipc.cpp @@ -184,9 +184,9 @@ BOOL TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::GetLogonSID(HANDLE hToken, if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto Cleanup; - ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), + ptg = static_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, - dwLength); + dwLength)); if (ptg == NULL) goto Cleanup; @@ -213,14 +213,14 @@ BOOL TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::GetLogonSID(HANDLE hToken, // Found the logon SID; make a copy of it. dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid); - *ppsid = (PSID)HeapAlloc(GetProcessHeap(), + *ppsid = static_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, - dwLength); + dwLength)); if (*ppsid == NULL) goto Cleanup; if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid)) { - HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid); + HeapFree(GetProcessHeap(), 0, static_cast(*ppsid)); goto Cleanup; } break; @@ -233,7 +233,7 @@ Cleanup: // Free the buffer for the token groups. if (ptg != NULL) - HeapFree(GetProcessHeap(), 0, (LPVOID)ptg); + HeapFree(GetProcessHeap(), 0, static_cast(ptg)); return bSuccess; } @@ -241,7 +241,7 @@ Cleanup: VOID TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::FreeLogonSID(PSID* ppsid) { // From https://learn.microsoft.com/previous-versions/aa446670(v=vs.85) - HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid); + HeapFree(GetProcessHeap(), 0, static_cast(*ppsid)); } int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_restricted_token(HANDLE handle, HANDLE token) @@ -279,7 +279,7 @@ int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_r ea.grfInheritance = NO_INHERITANCE; ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; ea.Trustee.TrusteeType = TRUSTEE_IS_USER; - ea.Trustee.ptstrName = (LPTSTR)user_restricted; + ea.Trustee.ptstrName = static_cast(user_restricted); if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) { @@ -302,9 +302,9 @@ int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_r error = 0; Lclean_dacl: - LocalFree((HLOCAL)new_dacl); + LocalFree(static_cast(new_dacl)); Lclean_sd: - LocalFree((HLOCAL)sd); + LocalFree(static_cast(sd)); Lclean_sid: FreeLogonSID(&user_restricted); Ldone: diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h index b424212314..fb64a11eae 100644 --- a/src/common/logger/logger_settings.h +++ b/src/common/logger/logger_settings.h @@ -47,6 +47,7 @@ struct LogSettings inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.txt"; inline const static std::string findMyMouseLoggerName = "find-my-mouse"; inline const static std::string mouseHighlighterLoggerName = "mouse-highlighter"; + inline const static std::string mouseJumpLoggerName = "mouse-jump"; inline const static std::string mousePointerCrosshairsLoggerName = "mouse-pointer-crosshairs"; inline const static std::string imageResizerLoggerName = "imageresizer"; inline const static std::string powerRenameLoggerName = "powerrename"; @@ -56,6 +57,8 @@ struct LogSettings inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt"; inline const static std::string hostsLoggerName = "hosts"; inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt"; + inline const static std::string registryPreviewLoggerName = "registrypreview"; + inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.txt"; inline const static int retention = 30; std::wstring logLevel; LogSettings(); diff --git a/src/common/notifications/notifications.cpp b/src/common/notifications/notifications.cpp index 7f03af0633..f6d5dbb11b 100644 --- a/src/common/notifications/notifications.cpp +++ b/src/common/notifications/notifications.cpp @@ -206,7 +206,7 @@ void notifications::show_toast(std::wstring message, std::wstring title, toast_p show_toast_with_activations(std::move(message), std::move(title), {}, {}, std::move(params)); } -inline void xml_escape(std::wstring data) +constexpr inline void xml_escape(std::wstring data) { std::wstring buffer; buffer.reserve(data.size()); diff --git a/src/common/updating/updating.cpp b/src/common/updating/updating.cpp index 3bb560319a..c0e194403b 100644 --- a/src/common/updating/updating.cpp +++ b/src/common/updating/updating.cpp @@ -8,6 +8,9 @@ #include #include +#include + +using namespace registry::install_scope; namespace // Strings in this namespace should not be localized { @@ -42,9 +45,16 @@ namespace updating std::pair extract_installer_asset_download_info(const json::JsonObject& release_object) { const std::wstring_view required_architecture = get_architecture_string(get_current_architecture()); - constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN; + std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN; // Desc-sorted by its priority const std::array asset_extensions = { L".exe", L".msi" }; + + const InstallScope current_install_scope = get_current_install_scope(); + if (current_install_scope == InstallScope::PerUser) + { + required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN_USER; + } + for (const auto asset_extension : asset_extensions) { for (auto asset_elem : release_object.GetNamedArray(L"assets")) diff --git a/src/common/updating/updating.h b/src/common/updating/updating.h index a5a15e47de..11ab3b95fd 100644 --- a/src/common/updating/updating.h +++ b/src/common/updating/updating.h @@ -31,4 +31,5 @@ namespace updating // non-localized constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup"; + constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN_USER = L"powertoysusersetup"; } diff --git a/src/common/updating/updating.vcxproj b/src/common/updating/updating.vcxproj index b68206c358..04625c09cf 100644 --- a/src/common/updating/updating.vcxproj +++ b/src/common/updating/updating.vcxproj @@ -15,6 +15,7 @@ v143 + diff --git a/src/common/utils/HDropIterator.h b/src/common/utils/HDropIterator.h index 3b52d65bab..11ffab5863 100644 --- a/src/common/utils/HDropIterator.h +++ b/src/common/utils/HDropIterator.h @@ -19,7 +19,7 @@ public: if (SUCCEEDED(pDataObject->GetData(&formatetc, &m_medium))) { - _listCount = DragQueryFile((HDROP)m_medium.hGlobal, 0xFFFFFFFF, NULL, 0); + _listCount = DragQueryFile(static_cast(m_medium.hGlobal), 0xFFFFFFFF, NULL, 0); } else { @@ -52,10 +52,10 @@ public: LPTSTR CurrentItem() const { - UINT cch = DragQueryFile((HDROP)m_medium.hGlobal, _current, NULL, 0) + 1; - LPTSTR pszPath = (LPTSTR)malloc(sizeof(TCHAR) * cch); + UINT cch = DragQueryFile(static_cast(m_medium.hGlobal), _current, NULL, 0) + 1; + LPTSTR pszPath = static_cast(malloc(sizeof(TCHAR) * cch)); - DragQueryFile((HDROP)m_medium.hGlobal, _current, pszPath, cch); + DragQueryFile(static_cast(m_medium.hGlobal), _current, pszPath, cch); return pszPath; } diff --git a/src/common/utils/HttpClient.h b/src/common/utils/HttpClient.h index 86b201a5d1..8726368fbb 100644 --- a/src/common/utils/HttpClient.h +++ b/src/common/utils/HttpClient.h @@ -57,7 +57,7 @@ namespace http totalBytesRead += buffer.Length(); if (progressUpdateCallback) { - float percentage = (float)totalBytesRead / totalBytes; + float percentage = static_cast(totalBytesRead) / totalBytes; progressUpdateCallback(percentage); } diff --git a/src/common/utils/MsiUtils.h b/src/common/utils/MsiUtils.h index 1489685005..f369dd3b6f 100644 --- a/src/common/utils/MsiUtils.h +++ b/src/common/utils/MsiUtils.h @@ -12,14 +12,16 @@ namespace // Strings in this namespace should not be localized { const inline wchar_t POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"; + const inline wchar_t POWER_TOYS_UPGRADE_CODE_USER[] = L"{D8B559DB-4C98-487A-A33F-50A8EEE42726}"; const inline wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; } -std::optional GetMsiPackageInstalledPath() +std::optional GetMsiPackageInstalledPath(bool perUser) { constexpr size_t guid_length = 39; wchar_t product_ID[guid_length]; - if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, product_ID); !found) + std::wstring upgradeCode = (perUser ? POWER_TOYS_UPGRADE_CODE_USER : POWER_TOYS_UPGRADE_CODE); + if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(upgradeCode.c_str(), 0, 0, product_ID); !found) { return std::nullopt; } diff --git a/src/common/utils/UnhandledExceptionHandler.h b/src/common/utils/UnhandledExceptionHandler.h index a3a69b2932..bbe2e81f24 100644 --- a/src/common/utils/UnhandledExceptionHandler.h +++ b/src/common/utils/UnhandledExceptionHandler.h @@ -93,7 +93,7 @@ inline std::wstring GetModuleName(HANDLE process, const STACKFRAME64& stack) return std::wstring(); } - if (!GetModuleFileNameW((HINSTANCE)moduleBase, modulePath, MAX_PATH)) + if (!GetModuleFileNameW(reinterpret_cast(moduleBase), modulePath, MAX_PATH)) { Logger::error(L"Failed to get a module path. {}", get_last_error_or_default(GetLastError())); return std::wstring(); @@ -105,7 +105,7 @@ inline std::wstring GetModuleName(HANDLE process, const STACKFRAME64& stack) inline std::wstring GetName(HANDLE process, const STACKFRAME64& stack) { - static IMAGEHLP_SYMBOL64* pSymbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR)); + static IMAGEHLP_SYMBOL64* pSymbol = static_cast(malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(TCHAR))); if (!pSymbol) { return std::wstring(); diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h index 2615d4b8ad..269f395d44 100644 --- a/src/common/utils/elevation.h +++ b/src/common/utils/elevation.h @@ -198,7 +198,7 @@ inline bool drop_elevated_privileges() TOKEN_MANDATORY_LABEL label = { 0 }; label.Label.Attributes = SE_GROUP_INTEGRITY; label.Label.Sid = medium_sid; - DWORD size = (DWORD)sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid); + DWORD size = static_cast(sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(medium_sid)); BOOL result = SetTokenInformation(token, TokenIntegrityLevel, &label, size); LocalFree(medium_sid); @@ -294,8 +294,9 @@ inline bool run_non_elevated(const std::wstring& file, const std::wstring& param siex.StartupInfo.cb = sizeof(siex); PROCESS_INFORMATION pi = { 0 }; + auto succeeded = CreateProcessW(file.c_str(), - const_cast(executable_args.c_str()), + &executable_args[0], nullptr, nullptr, FALSE, @@ -395,8 +396,9 @@ inline bool run_same_elevation(const std::wstring& file, const std::wstring& par STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi = { 0 }; + auto succeeded = CreateProcessW(file.c_str(), - const_cast(executable_args.c_str()), + &executable_args[0], nullptr, nullptr, FALSE, @@ -464,7 +466,7 @@ inline bool check_user_is_admin() } // Allocate the buffer. - pGroupInfo = (PTOKEN_GROUPS)GlobalAlloc(GPTR, dwSize); + pGroupInfo = static_cast(GlobalAlloc(GPTR, dwSize)); // Call GetTokenInformation again to get the group information. if (!GetTokenInformation(hToken, TokenGroups, pGroupInfo, dwSize, &dwSize)) diff --git a/src/common/utils/exec.h b/src/common/utils/exec.h index feeda08b89..ec76c92876 100644 --- a/src/common/utils/exec.h +++ b/src/common/utils/exec.h @@ -4,8 +4,11 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// disable warning 26492 - Don't use const_cast to cast away const +// disable warning 26493 - Don't use C-style casts +// Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable: 26471) +#pragma warning(disable : 26471 26492 26493 26497) #include #pragma warning(pop) diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index c77c036c51..e105749812 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -38,6 +38,7 @@ namespace powertoys_gpo { const std::wstring POLICY_CONFIGURE_ENABLED_KEYBOARD_MANAGER = L"ConfigureEnabledUtilityKeyboardManager"; const std::wstring POLICY_CONFIGURE_ENABLED_FIND_MY_MOUSE = L"ConfigureEnabledUtilityFindMyMouse"; const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_HIGHLIGHTER = L"ConfigureEnabledUtilityMouseHighlighter"; + const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_JUMP = L"ConfigureEnabledUtilityMouseJump"; const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_POINTER_CROSSHAIRS = L"ConfigureEnabledUtilityMousePointerCrosshairs"; const std::wstring POLICY_CONFIGURE_ENABLED_POWER_RENAME = L"ConfigureEnabledUtilityPowerRename"; const std::wstring POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER = L"ConfigureEnabledUtilityPowerLauncher"; @@ -45,12 +46,23 @@ namespace powertoys_gpo { const std::wstring POLICY_CONFIGURE_ENABLED_SCREEN_RULER = L"ConfigureEnabledUtilityScreenRuler"; const std::wstring POLICY_CONFIGURE_ENABLED_SHORTCUT_GUIDE = L"ConfigureEnabledUtilityShortcutGuide"; const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor"; + const std::wstring POLICY_CONFIGURE_ENABLED_PASTE_PLAIN = L"ConfigureEnabledUtilityPastePlain"; const std::wstring POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE = L"ConfigureEnabledUtilityVideoConferenceMute"; + const std::wstring POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW = L"ConfigureEnabledUtilityRegistryPreview"; + + // The registry value names for PowerToys installer and update policies. + const std::wstring POLICY_DISABLE_PER_USER_INSTALLATION = L"PerUserInstallationDisabled"; + const std::wstring POLICY_DISABLE_AUTOMATIC_UPDATE_DOWNLOAD = L"AutomaticUpdateDownloadDisabled"; + const std::wstring POLICY_SUSPEND_NEW_UPDATE_TOAST = L"SuspendNewUpdateAvailableToast"; + const std::wstring POLICY_DISABLE_PERIODIC_UPDATE_CHECK = L"PeriodicUpdateCheckDisabled"; + + // The registry value names for other PowerToys policies. + const std::wstring POLICY_ALLOW_EXPERIMENTATION = L"AllowExperimentation"; inline gpo_rule_configured_t getConfiguredValue(const std::wstring& registry_value_name) { HKEY key{}; - DWORD value = (DWORD) -2; + DWORD value = 0xFFFFFFFE; DWORD valueSize = sizeof(value); bool machine_key_found = true; @@ -62,7 +74,7 @@ namespace powertoys_gpo { if(machine_key_found) { // If the path was found in the machine, we need to check if the value for the policy exists. - auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, (LPBYTE)&value, &valueSize); + auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast(&value), &valueSize); RegCloseKey(key); @@ -82,7 +94,7 @@ namespace powertoys_gpo { } return gpo_rule_configured_unavailable; } - auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, (LPBYTE)&value, &valueSize); + auto res = RegQueryValueExW(key, registry_value_name.c_str(), nullptr, nullptr, reinterpret_cast(&value), &valueSize); RegCloseKey(key); if (res != ERROR_SUCCESS) { @@ -195,6 +207,11 @@ namespace powertoys_gpo { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_MOUSE_HIGHLIGHTER); } + inline gpo_rule_configured_t getConfiguredMouseJumpEnabledValue() + { + return getConfiguredValue(POLICY_CONFIGURE_ENABLED_MOUSE_JUMP); + } + inline gpo_rule_configured_t getConfiguredMousePointerCrosshairsEnabledValue() { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_MOUSE_POINTER_CROSSHAIRS); @@ -230,9 +247,44 @@ namespace powertoys_gpo { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR); } + inline gpo_rule_configured_t getConfiguredPastePlainEnabledValue() + { + return getConfiguredValue(POLICY_CONFIGURE_ENABLED_PASTE_PLAIN); + } + inline gpo_rule_configured_t getConfiguredVideoConferenceMuteEnabledValue() { return getConfiguredValue(POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE); } + inline gpo_rule_configured_t getConfiguredRegistryPreviewEnabledValue() + { + return getConfiguredValue(POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW); + } + + inline gpo_rule_configured_t getDisablePerUserInstallationValue() + { + return getConfiguredValue(POLICY_DISABLE_PER_USER_INSTALLATION); + } + + inline gpo_rule_configured_t getDisableAutomaticUpdateDownloadValue() + { + return getConfiguredValue(POLICY_DISABLE_AUTOMATIC_UPDATE_DOWNLOAD); + } + + inline gpo_rule_configured_t getSuspendNewUpdateToastValue() + { + return getConfiguredValue(POLICY_SUSPEND_NEW_UPDATE_TOAST); + } + + inline gpo_rule_configured_t getDisablePeriodicUpdateCheckValue() + { + return getConfiguredValue(POLICY_DISABLE_PERIODIC_UPDATE_CHECK); + } + + inline gpo_rule_configured_t getAllowExperimentationValue() + { + return getConfiguredValue(POLICY_ALLOW_EXPERIMENTATION); + } + } diff --git a/src/common/utils/modulesRegistry.h b/src/common/utils/modulesRegistry.h index 62d0d35cf2..1f58ab55ca 100644 --- a/src/common/utils/modulesRegistry.h +++ b/src/common/utils/modulesRegistry.h @@ -192,6 +192,24 @@ inline registry::ChangeSet getStlThumbnailHandlerChangeSet(const std::wstring in NonLocalizable::ExtSTL); } +inline registry::ChangeSet getRegistryPreviewChangeSet(const std::wstring installationDir,const bool perUser) +{ + const HKEY scope = perUser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + + using vec_t = std::vector; + vec_t changes; + + std::wstring command = installationDir; + command.append(L"\\modules\\RegistryPreview\\PowerToys.RegistryPreview.exe \"%1\""); + changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview\\command", std::nullopt, command }); + + std::wstring icon_path = installationDir; + icon_path.append(L"\\modules\\RegistryPreview\\app.ico"); + changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview", L"icon", icon_path }); + + return { changes }; +} + inline std::vector getAllOnByDefaultModulesChangeSets(const std::wstring installationDir) { constexpr bool PER_USER = true; @@ -201,7 +219,8 @@ inline std::vector getAllOnByDefaultModulesChangeSets(const getGcodePreviewHandlerChangeSet(installationDir, PER_USER), getSvgThumbnailHandlerChangeSet(installationDir, PER_USER), getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER), - getStlThumbnailHandlerChangeSet(installationDir, PER_USER) }; + getStlThumbnailHandlerChangeSet(installationDir, PER_USER), + getRegistryPreviewChangeSet(installationDir, PER_USER) }; } inline std::vector getAllModulesChangeSets(const std::wstring installationDir) @@ -215,5 +234,6 @@ inline std::vector getAllModulesChangeSets(const std::wstri getSvgThumbnailHandlerChangeSet(installationDir, PER_USER), getPdfThumbnailHandlerChangeSet(installationDir, PER_USER), getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER), - getStlThumbnailHandlerChangeSet(installationDir, PER_USER) }; + getStlThumbnailHandlerChangeSet(installationDir, PER_USER), + getRegistryPreviewChangeSet(installationDir, PER_USER) }; } diff --git a/src/common/utils/process_path.h b/src/common/utils/process_path.h index 382a1d2ea5..56129a9bb7 100644 --- a/src/common/utils/process_path.h +++ b/src/common/utils/process_path.h @@ -15,7 +15,7 @@ inline std::wstring get_process_path(DWORD pid) noexcept { name.resize(MAX_PATH); DWORD name_length = static_cast(name.length()); - if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0) + if (QueryFullProcessImageNameW(process, 0, name.data(), &name_length) == 0) { name_length = 0; } @@ -118,5 +118,5 @@ inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool remo { PathRemoveFileSpecW(buffer); } - return { buffer, (UINT)lstrlenW(buffer) }; + return { buffer, static_cast(lstrlenW(buffer))}; } diff --git a/src/common/utils/registry.h b/src/common/utils/registry.h index d254457999..57507e36b1 100644 --- a/src/common/utils/registry.h +++ b/src/common/utils/registry.h @@ -16,6 +16,86 @@ namespace registry { + namespace install_scope + { + const wchar_t INSTALL_SCOPE_REG_KEY[] = L"Software\\Classes\\powertoys\\"; + + enum class InstallScope + { + PerMachine = 0, + PerUser, + }; + + inline const InstallScope get_current_install_scope() + { + // Open HKLM key + HKEY perMachineKey{}; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + INSTALL_SCOPE_REG_KEY, + 0, + KEY_READ, + &perMachineKey) != ERROR_SUCCESS) + { + // Open HKCU key + HKEY perUserKey{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, + INSTALL_SCOPE_REG_KEY, + 0, + KEY_READ, + &perUserKey) != ERROR_SUCCESS) + { + // both keys are missing + return InstallScope::PerMachine; + } + else + { + DWORD dataSize{}; + if (RegGetValueW( + perUserKey, + nullptr, + L"InstallScope", + RRF_RT_REG_SZ, + nullptr, + nullptr, + &dataSize) != ERROR_SUCCESS) + { + // HKCU key is missing + RegCloseKey(perUserKey); + return InstallScope::PerMachine; + } + + std::wstring data; + data.resize(dataSize / sizeof(wchar_t)); + + if (RegGetValueW( + perUserKey, + nullptr, + L"InstallScope", + RRF_RT_REG_SZ, + nullptr, + &data[0], + &dataSize) != ERROR_SUCCESS) + { + // HKCU key is missing + RegCloseKey(perUserKey); + return InstallScope::PerMachine; + } + RegCloseKey(perUserKey); + + if (data.contains(L"perUser")) + { + return InstallScope::PerUser; + } + } + } + + return InstallScope::PerMachine; + } + } + + template + inline constexpr bool always_false_v = false; + namespace detail { struct on_exit @@ -27,9 +107,6 @@ namespace registry ~on_exit() { f(); } }; - template - inline constexpr bool always_false_v = false; - template struct overloaded : Ts... { @@ -241,7 +318,7 @@ namespace registry switch (type) { case REG_DWORD: - return *(DWORD*)buffer; + return *reinterpret_cast(buffer); case REG_SZ: { if (!valueSize) diff --git a/src/common/utils/timeutil.h b/src/common/utils/timeutil.h index 8242cef9d0..b82e7981bd 100644 --- a/src/common/utils/timeutil.h +++ b/src/common/utils/timeutil.h @@ -60,7 +60,7 @@ namespace timeutil inline int64_t in_days(const std::time_t to, const std::time_t from) { - return static_cast(std::difftime(to, from) / (3600 * (int64_t)24)); + return static_cast(std::difftime(to, from) / (3600 * 24LL)); } } } diff --git a/src/common/utils/winapi_error.h b/src/common/utils/winapi_error.h index b4cc01aa83..dec9f93146 100644 --- a/src/common/utils/winapi_error.h +++ b/src/common/utils/winapi_error.h @@ -35,7 +35,7 @@ inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const { return; } - LPWSTR lpDisplayBuf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR)); + LPWSTR lpDisplayBuf = static_cast(LocalAlloc(LMEM_ZEROINIT, (system_message->size() + lstrlenW(functionName) + 40) * sizeof(WCHAR))); if (lpDisplayBuf != NULL) { StringCchPrintfW(lpDisplayBuf, @@ -44,7 +44,7 @@ inline void show_last_error_message(const wchar_t* functionName, DWORD dw, const functionName, system_message->c_str(), dw); - MessageBoxW(NULL, (LPCTSTR)lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR); + MessageBoxW(NULL, lpDisplayBuf, errorTitle, MB_OK | MB_ICONERROR); LocalFree(lpDisplayBuf); } } diff --git a/src/common/utils/window.h b/src/common/utils/window.h index 36e4b671c3..cfcf20534a 100644 --- a/src/common/utils/window.h +++ b/src/common/utils/window.h @@ -60,7 +60,7 @@ template inline T GetWindowCreateParam(LPARAM lparam) { static_assert(sizeof(T) <= sizeof(void*)); - T data{ (T)(reinterpret_cast(lparam)->lpCreateParams) }; + T data{ static_cast (reinterpret_cast(lparam)->lpCreateParams) }; return data; } @@ -74,5 +74,5 @@ inline void StoreWindowParam(HWND window, T data) template inline T GetWindowParam(HWND window) { - return (T)GetWindowLongPtrW(window, GWLP_USERDATA); + return reinterpret_cast (GetWindowLongPtrW(window, GWLP_USERDATA)); } diff --git a/src/common/version/version.h b/src/common/version/version.h index 5e78481c42..8c3ffd302d 100644 --- a/src/common/version/version.h +++ b/src/common/version/version.h @@ -13,7 +13,7 @@ #define PRODUCT_VERSION_STRING FILE_VERSION_STRING #define COMPANY_NAME "Microsoft Corporation" -#define COPYRIGHT_NOTE "Copyright (C) 2022 Microsoft Corporation" +#define COPYRIGHT_NOTE "Copyright (C) 2023 Microsoft Corporation" #define PRODUCT_NAME "PowerToys" #include diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index 037bf1cc2d..65f8b55a26 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -1,18 +1,22 @@ - + - + + + - + + + @@ -216,6 +220,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -246,7 +270,17 @@ - + + + + + + + + + + + @@ -285,6 +319,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index 9a07cd42f9..a551c8b4ba 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -1,14 +1,17 @@ - + PowerToys PowerToys Microsoft PowerToys + Installer and Updates PowerToys version 0.64.0 or later + PowerToys version 0.68.0 or later + PowerToys version 0.69.0 or later This policy configures the enabled state for a PowerToys utility. @@ -27,6 +30,38 @@ If you enable this setting, the utility will be always enabled and the user won' If you disable this setting, the utility will be always disabled and the user won't be able to enable it. If you don't configure this setting, users are able to disable or enable the utility. + + This policy configures whether per-user PowerToys installation is allowed or not. + +If enabled, per-user installation is not allowed. + +If disabled or not configured, per-user installation is allowed. + + This policy configures whether automatic downloads of available updates are disabled or not. (On metered connections updates are never downloaded.) + +If enabled, automatic downloads are disabled. + +If disabled or not configured, the user is in control of automatic downloads setting. + + This policy configures whether the action center notification for new updates is suspended for 2 minor releases. (Example: if the installed version is v0.60.0, then the next notification is shown for the v0.63.* release.) + +If enabled, the notification is suspended. + +If disabled or not configured, the notification is shown. + +Note: The notification about new major versions is always displayed. + + This policy allows you to disable automatic update checks running in the background. (The manual check in PT Settings is not affected by this policy.) + +If enabled, the automatic update checks are disabled. + +If disabled or not configured, the automatic update checks are enabled. + + This policy configures whether PowerToys experimentation is allowed. With experimentation allowed the user sees the new features being experimented if it gets selected as part of the test group. (Experimentation will only happen on Windows Insider builds.) + +If this setting is not configured or enabled, the user can control experimentation in the PowerToys settings menu. + +If this setting is disabled, experimentation is not allowed. Always On Top: Configure enabled state Awake: Configure enabled state @@ -47,14 +82,22 @@ If you don't configure this setting, users are able to disable or enable the uti Keyboard Manager: Configure enabled state Find My Mouse: Configure enabled state Mouse Highlighter: Configure enabled state + Mouse Jump: Configure enabled state Mouse Pointer Crosshairs: Configure enabled state + Paste as Plain Text: Configure enabled state Power Rename: Configure enabled state PowerToys Run: Configure enabled state Quick Accent: Configure enabled state + Registry Preview: Configure enabled state Screen Ruler: Configure enabled state Shortcut Guide: Configure enabled state Text Extractor: Configure enabled state Video Conference Mute: Configure enabled state + Disable per-user installation + Disable automatic downloads + Suspend Action Center notification for new updates + Disable automatic update checks + Allow Experimentation diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp index 6db892d2fb..5015d2d9e3 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp @@ -7,6 +7,9 @@ #include "Trace.h" #include "Generated Files/resource.h" +#include +#include + // Implementations of inherited IUnknown methods IFACEMETHODIMP ExplorerCommand::QueryInterface(REFIID riid, void** ppv) @@ -46,9 +49,10 @@ IFACEMETHODIMP ExplorerCommand::GetTitle(IShellItemArray* psiItemArray, LPWSTR* IFACEMETHODIMP ExplorerCommand::GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon) { - // Path to the icon should be computed relative to the path of this module - ppszIcon = NULL; - return E_NOTIMPL; + std::wstring iconResourcePath = get_module_filename(); + iconResourcePath += L",-"; + iconResourcePath += std::to_wstring(IDI_FILELOCKSMITH); + return SHStrDup(iconResourcePath.c_str(), ppszIcon); } IFACEMETHODIMP ExplorerCommand::GetToolTip(IShellItemArray* psiItemArray, LPWSTR* ppszInfotip) @@ -129,7 +133,18 @@ IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UI mii.fState = MFS_ENABLED; - // TODO icon from file + // icon from file + HICON hIcon = static_cast(LoadImage(globals::instance, MAKEINTRESOURCE(IDI_FILELOCKSMITH), IMAGE_ICON, 16, 16, 0)); + if (hIcon) + { + mii.fMask |= MIIM_BITMAP; + if (m_hbmpIcon == NULL) + { + m_hbmpIcon = CreateBitmapFromIcon(hIcon); + } + mii.hbmpItem = m_hbmpIcon; + DestroyIcon(hIcon); + } if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii)) { @@ -226,30 +241,6 @@ ExplorerCommand::~ExplorerCommand() --globals::ref_count; } -// Implementation taken from src/common/utils -// TODO reference that function -inline std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool removeFilename = true) -{ - wchar_t buffer[MAX_PATH + 1]; - DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH + 1); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - const DWORD long_path_length = 0xFFFF; // should be always enough - std::wstring long_filename(long_path_length, L'\0'); - actual_length = GetModuleFileNameW(mod, long_filename.data(), long_path_length); - PathRemoveFileSpecW(long_filename.data()); - long_filename.resize(std::wcslen(long_filename.data())); - long_filename.shrink_to_fit(); - return long_filename; - } - - if (removeFilename) - { - PathRemoveFileSpecW(buffer); - } - return { buffer, (UINT)lstrlenW(buffer) }; -} - HRESULT ExplorerCommand::LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer) { // Compute exe path diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h index 5d6e2aad40..9da12236be 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h @@ -42,6 +42,8 @@ public: ~ExplorerCommand(); private: + HBITMAP m_hbmpIcon = nullptr; + // Helpers HRESULT LaunchUI(CMINVOKECOMMANDINFO* pici, ipc::Writer* writer); diff --git a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc index b55a2b37ad..ea565d98f9 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc +++ b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.base.rc @@ -48,3 +48,10 @@ BEGIN VALUE "Translation", 0x409, 1200 END END + +// Non-localizable +////////////////////////////// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_FILELOCKSMITH ICON "..\\FileLocksmithUI\\Assets\\Icon.ico" \ No newline at end of file diff --git a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj index 4e69eba166..d21a9eb775 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj @@ -280,6 +280,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {98537082-0fdb-40de-abd8-0dc5a4269bab} + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} diff --git a/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h b/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h index 1ffbbe0caa..955a04e15b 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/resource.base.h @@ -8,6 +8,7 @@ #define FILE_DESCRIPTION "PowerToys File Locksmith Static Library" #define INTERNAL_NAME "PowerToys.FileLocksmithLib.lib" #define ORIGINAL_FILENAME "PowerToys.FileLocksmithLib.lib" +#define IDI_FILELOCKSMITH 1001 // Non-localizable ////////////////////////////// diff --git a/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp b/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp index 3968348997..cc0fce4a79 100644 --- a/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp +++ b/src/modules/FileLocksmith/FileLocksmithLibInterop/interop.cpp @@ -151,7 +151,7 @@ namespace FileLocksmith::Interop if (!stream) { - return false; + return false; } stream.close(); @@ -178,7 +178,7 @@ namespace FileLocksmith::Interop return false; } - /* Adapted from "https://learn.microsoft.com/en-us/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--" */ + /* Adapted from "https://learn.microsoft.com/windows/win32/secauthz/enabling-and-disabling-privileges-in-c--" */ static System::Boolean SetDebugPrivilege() { HANDLE hToken; diff --git a/src/modules/FileLocksmith/FileLocksmithUI/App.xaml.cs b/src/modules/FileLocksmith/FileLocksmithUI/App.xaml.cs index a55714470a..15312c5969 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/App.xaml.cs +++ b/src/modules/FileLocksmith/FileLocksmithUI/App.xaml.cs @@ -4,7 +4,6 @@ using System; using System.Linq; -using FileLocksmithUI.Helpers; using ManagedCommon; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; @@ -23,6 +22,8 @@ namespace FileLocksmithUI /// public App() { + Logger.InitializeLogger("\\File Locksmith\\FileLocksmithUI\\Logs"); + this.InitializeComponent(); } diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj index 094cd7f13c..c2dfe3edfe 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj +++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj @@ -62,15 +62,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/modules/FileLocksmith/FileLocksmithUI/Helpers/Logger.cs b/src/modules/FileLocksmith/FileLocksmithUI/Helpers/Logger.cs deleted file mode 100644 index 2ec357c3cf..0000000000 --- a/src/modules/FileLocksmith/FileLocksmithUI/Helpers/Logger.cs +++ /dev/null @@ -1,76 +0,0 @@ -// 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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; - -namespace FileLocksmithUI.Helpers -{ - public static class Logger - { - private static readonly string ApplicationLogPath = Path.Combine(interop.Constants.AppDataPath(), "File Locksmith\\FileLocksmithUI\\Logs"); - - static Logger() - { - if (!Directory.Exists(ApplicationLogPath)) - { - Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs b/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs index 6e0adf2dcf..d473a05c58 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs +++ b/src/modules/FileLocksmith/FileLocksmithUI/ViewModels/MainViewModel.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FileLocksmith.Interop; -using global::FileLocksmithUI.Helpers; +using ManagedCommon; namespace PowerToys.FileLocksmithUI.ViewModels { diff --git a/src/modules/FileLocksmith/FileLocksmithUI/app.manifest b/src/modules/FileLocksmith/FileLocksmithUI/app.manifest new file mode 100644 index 0000000000..575aa4df9d --- /dev/null +++ b/src/modules/FileLocksmith/FileLocksmithUI/app.manifest @@ -0,0 +1,21 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + + + + + + + diff --git a/src/modules/Hosts/Hosts.Tests/EntryTest.cs b/src/modules/Hosts/Hosts.Tests/EntryTest.cs index 7a61fe147c..c316ecda53 100644 --- a/src/modules/Hosts/Hosts.Tests/EntryTest.cs +++ b/src/modules/Hosts/Hosts.Tests/EntryTest.cs @@ -28,7 +28,7 @@ namespace Hosts.Tests [DataRow("# # \t10.1.1.1 host#comment", "10.1.1.1", "host", "comment", false)] public void Valid_Entry_SingleHost(string line, string address, string host, string comment, bool active) { - var entry = new Entry(line); + var entry = new Entry(0, line); Assert.AreEqual(entry.Address, address); Assert.AreEqual(entry.Hosts, host); @@ -52,7 +52,7 @@ namespace Hosts.Tests [DataRow("#10.1.1.1 host host.local#comment", "10.1.1.1", "host host.local", "comment", false)] public void Valid_Entry_MultipleHosts(string line, string address, string host, string comment, bool active) { - var entry = new Entry(line); + var entry = new Entry(0, line); Assert.AreEqual(entry.Address, address); Assert.AreEqual(entry.Hosts, host); @@ -76,7 +76,7 @@ namespace Hosts.Tests [DataRow("host 10.1.1.1")] public void Not_Valid_Entry(string line) { - var entry = new Entry(line); + var entry = new Entry(0, line); Assert.IsFalse(entry.Valid); } } diff --git a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj index 40c7baf060..dc75cf4b59 100644 --- a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj +++ b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj @@ -14,13 +14,13 @@ - - - - - - - + + + + + + + diff --git a/src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs b/src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs index 1de3d52bc2..7756c434d3 100644 --- a/src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs +++ b/src/modules/Hosts/Hosts.Tests/HostsServiceTest.cs @@ -70,7 +70,7 @@ namespace Hosts.Tests fileSystem.AddFile(service.HostsFilePath, new MockFileData(content)); var (_, entries) = await service.ReadAsync(); - entries.Add(new Entry("10.1.1.30", "host30 host30.local", "new entry", false)); + entries.Add(new Entry(0, "10.1.1.30", "host30 host30.local", "new entry", false)); await service.WriteAsync(string.Empty, entries); var result = fileSystem.GetFile(service.HostsFilePath); diff --git a/src/modules/Hosts/Hosts/Helpers/ExpressionExtensions.cs b/src/modules/Hosts/Hosts/Helpers/ExpressionExtensions.cs new file mode 100644 index 0000000000..a72265ab24 --- /dev/null +++ b/src/modules/Hosts/Hosts/Helpers/ExpressionExtensions.cs @@ -0,0 +1,43 @@ +// 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. + +using System; +using System.Linq.Expressions; + +namespace Hosts.Helpers +{ + // https://stackoverflow.com/a/22569086 + public static class ExpressionExtensions + { + public static Expression> And( + this Expression> expr1, + Expression> expr2) + { + var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]); + return Expression.Lambda>(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters); + } + + public static Expression Replace(this Expression expression, Expression searchEx, Expression replaceEx) + { + return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); + } + + internal sealed class ReplaceVisitor : ExpressionVisitor + { + private readonly Expression _from; + private readonly Expression _to; + + public ReplaceVisitor(Expression from, Expression to) + { + _from = from; + _to = to; + } + + public override Expression Visit(Expression node) + { + return node == _from ? _to : base.Visit(node); + } + } + } +} diff --git a/src/modules/Hosts/Hosts/Helpers/HostsService.cs b/src/modules/Hosts/Hosts/Helpers/HostsService.cs index 2e117446ad..7d90e57144 100644 --- a/src/modules/Hosts/Hosts/Helpers/HostsService.cs +++ b/src/modules/Hosts/Hosts/Helpers/HostsService.cs @@ -15,6 +15,7 @@ using System.Threading; using System.Threading.Tasks; using Hosts.Models; using Hosts.Settings; +using ManagedCommon; using Microsoft.Win32; using Settings.UI.Library.Enumerations; @@ -82,7 +83,7 @@ namespace Hosts.Helpers continue; } - var entry = new Entry(line); + var entry = new Entry(i, line); if (entry.Valid) { diff --git a/src/modules/Hosts/Hosts/Helpers/Logger.cs b/src/modules/Hosts/Hosts/Helpers/Logger.cs deleted file mode 100644 index 8a4cd558ce..0000000000 --- a/src/modules/Hosts/Hosts/Helpers/Logger.cs +++ /dev/null @@ -1,80 +0,0 @@ -// 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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace Hosts.Helpers -{ - // TODO: use centralized logger https://github.com/microsoft/PowerToys/issues/19650 - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "Hosts\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/Hosts/Hosts/Helpers/NativeMethods.cs b/src/modules/Hosts/Hosts/Helpers/NativeMethods.cs index bff5f3db4f..9db941164b 100644 --- a/src/modules/Hosts/Hosts/Helpers/NativeMethods.cs +++ b/src/modules/Hosts/Hosts/Helpers/NativeMethods.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; namespace Hosts.Helpers { - internal class NativeMethods + internal sealed class NativeMethods { [DllImport("user32.dll", SetLastError = true)] internal static extern IntPtr SetForegroundWindow(IntPtr hWnd); diff --git a/src/modules/Hosts/Hosts/Hosts.csproj b/src/modules/Hosts/Hosts/Hosts.csproj index 0baa5bf015..36e0ac4436 100644 --- a/src/modules/Hosts/Hosts/Hosts.csproj +++ b/src/modules/Hosts/Hosts/Hosts.csproj @@ -46,15 +46,15 @@ - - - - - - - - - + + + + + + + + + @@ -64,6 +64,7 @@ + diff --git a/src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs b/src/modules/Hosts/Hosts/Models/AddressType.cs similarity index 60% rename from src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs rename to src/modules/Hosts/Hosts/Models/AddressType.cs index 1520662e47..f89a950218 100644 --- a/src/modules/awake/Awake/Core/Models/BatteryReportingScale.cs +++ b/src/modules/Hosts/Hosts/Models/AddressType.cs @@ -2,11 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Awake.Core.Models +namespace Hosts.Models { - public struct BatteryReportingScale + public enum AddressType { - public uint Granularity; - public uint Capacity; + Invalid = 0, + IPv4 = 1, + IPv6 = 2, } } diff --git a/src/modules/Hosts/Hosts/Models/Entry.cs b/src/modules/Hosts/Hosts/Models/Entry.cs index 11cebaeb73..23ce2518d6 100644 --- a/src/modules/Hosts/Hosts/Models/Entry.cs +++ b/src/modules/Hosts/Hosts/Models/Entry.cs @@ -22,6 +22,7 @@ namespace Hosts.Models set { SetProperty(ref _address, value); + SetAddressType(); OnPropertyChanged(nameof(Valid)); } } @@ -54,22 +55,28 @@ namespace Hosts.Models [ObservableProperty] private bool _duplicate; - public bool Valid => ValidationHelper.ValidHosts(_hosts) && (ValidationHelper.ValidIPv4(_address) || ValidationHelper.ValidIPv6(_address)); + public bool Valid => ValidationHelper.ValidHosts(_hosts) && Type != AddressType.Invalid; + + public AddressType Type { get; private set; } public string[] SplittedHosts { get; private set; } + public int Id { get; set; } + public Entry() { } - public Entry(string line) + public Entry(int id, string line) { + Id = id; _line = line.Trim(); Parse(); } - public Entry(string address, string hosts, string comment, bool active) + public Entry(int id, string address, string hosts, string comment, bool active) { + Id = id; Address = address.Trim(); Hosts = hosts.Trim(); Comment = comment.Trim(); @@ -151,5 +158,21 @@ namespace Hosts.Models { return _line; } + + private void SetAddressType() + { + if (ValidationHelper.ValidIPv4(_address)) + { + Type = AddressType.IPv4; + } + else if (ValidationHelper.ValidIPv6(_address)) + { + Type = AddressType.IPv6; + } + else + { + Type = AddressType.Invalid; + } + } } } diff --git a/src/modules/Hosts/Hosts/Program.cs b/src/modules/Hosts/Hosts/Program.cs index 32b46a7a44..4b41f3c71a 100644 --- a/src/modules/Hosts/Hosts/Program.cs +++ b/src/modules/Hosts/Hosts/Program.cs @@ -4,7 +4,7 @@ using System; using System.Threading; -using Hosts.Helpers; +using ManagedCommon; using Microsoft.UI.Dispatching; using Microsoft.Windows.AppLifecycle; @@ -15,6 +15,8 @@ namespace Hosts [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\Hosts\\Logs"); + WinRT.ComWrappersSupport.InitializeComWrappers(); if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredHostsFileEditorEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) diff --git a/src/modules/Hosts/Hosts/Settings/IUserSettings.cs b/src/modules/Hosts/Hosts/Settings/IUserSettings.cs index 5278d60a95..1f1d97caa4 100644 --- a/src/modules/Hosts/Hosts/Settings/IUserSettings.cs +++ b/src/modules/Hosts/Hosts/Settings/IUserSettings.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Settings.UI.Library.Enumerations; namespace Hosts.Settings @@ -10,6 +11,10 @@ namespace Hosts.Settings { public bool ShowStartupWarning { get; } + public bool LoopbackDuplicates { get; } + public AdditionalLinesPosition AdditionalLinesPosition { get; } + + event EventHandler LoopbackDuplicatesChanged; } } diff --git a/src/modules/Hosts/Hosts/Settings/UserSettings.cs b/src/modules/Hosts/Hosts/Settings/UserSettings.cs index 26a8e931e0..f1e47e6019 100644 --- a/src/modules/Hosts/Hosts/Settings/UserSettings.cs +++ b/src/modules/Hosts/Hosts/Settings/UserSettings.cs @@ -5,6 +5,7 @@ using System; using System.IO.Abstractions; using System.Threading; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Settings.UI.Library.Enumerations; @@ -22,12 +23,28 @@ namespace Hosts.Settings public bool ShowStartupWarning { get; private set; } + private bool _loopbackDuplicates; + + public bool LoopbackDuplicates + { + get => _loopbackDuplicates; + set + { + if (_loopbackDuplicates != value) + { + _loopbackDuplicates = value; + LoopbackDuplicatesChanged?.Invoke(this, EventArgs.Empty); + } + } + } + public AdditionalLinesPosition AdditionalLinesPosition { get; private set; } public UserSettings() { _settingsUtils = new SettingsUtils(); ShowStartupWarning = true; + LoopbackDuplicates = false; AdditionalLinesPosition = AdditionalLinesPosition.Top; LoadSettingsFromJson(); @@ -35,6 +52,8 @@ namespace Hosts.Settings _watcher = Helper.GetFileWatcher(HostsModuleName, "settings.json", () => LoadSettingsFromJson()); } + public event EventHandler LoopbackDuplicatesChanged; + private void LoadSettingsFromJson() { lock (_loadingSettingsLock) @@ -60,6 +79,7 @@ namespace Hosts.Settings { ShowStartupWarning = settings.Properties.ShowStartupWarning; AdditionalLinesPosition = settings.Properties.AdditionalLinesPosition; + LoopbackDuplicates = settings.Properties.LoopbackDuplicates; } retry = false; diff --git a/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs b/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs index 23bd229890..0896e9c115 100644 --- a/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs +++ b/src/modules/Hosts/Hosts/ViewModels/MainViewModel.cs @@ -6,14 +6,18 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; -using System.Windows.Input; using Common.UI; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.WinUI; +using CommunityToolkit.WinUI.UI; using Hosts.Helpers; using Hosts.Models; +using Hosts.Settings; +using ManagedCommon; using Microsoft.UI.Dispatching; namespace Hosts.ViewModels @@ -21,8 +25,18 @@ namespace Hosts.ViewModels public partial class MainViewModel : ObservableObject, IDisposable { private readonly IHostsService _hostsService; + private readonly IUserSettings _userSettings; private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + private readonly string[] _loopbackAddresses = + { + "127.0.0.1", + "::1", + "0:0:0:0:0:0:0:1", + }; + + private bool _readingHosts; private bool _disposed; + private CancellationTokenSource _tokenSource; [ObservableProperty] private Entry _selected; @@ -33,9 +47,6 @@ namespace Hosts.ViewModels [ObservableProperty] private bool _fileChanged; - [ObservableProperty] - private bool _filtered; - [ObservableProperty] private string _addressFilter; @@ -46,34 +57,39 @@ namespace Hosts.ViewModels private string _commentFilter; [ObservableProperty] - [NotifyPropertyChangedFor(nameof(Entries))] - private bool _showOnlyDuplicates; + private string _additionalLines; [ObservableProperty] - private string _additionalLines; + private bool _isLoading; + + [ObservableProperty] + private bool _filtered; + + private bool _showOnlyDuplicates; + + public bool ShowOnlyDuplicates + { + get => _showOnlyDuplicates; + set + { + SetProperty(ref _showOnlyDuplicates, value); + ApplyFilters(); + } + } private ObservableCollection _entries; - public ObservableCollection Entries => _filtered || _showOnlyDuplicates ? GetFilteredEntries() : _entries; + public AdvancedCollectionView Entries { get; set; } - public ICommand ReadHostsCommand => new RelayCommand(ReadHosts); + public int NextId => _entries.Max(e => e.Id) + 1; - public ICommand ApplyFiltersCommand => new RelayCommand(ApplyFilters); - - public ICommand ClearFiltersCommand => new RelayCommand(ClearFilters); - - public ICommand OpenSettingsCommand => new RelayCommand(OpenSettings); - - public ICommand OpenHostsFileCommand => new RelayCommand(OpenHostsFile); - - public MainViewModel(IHostsService hostService) + public MainViewModel(IHostsService hostService, IUserSettings userSettings) { _hostsService = hostService; + _userSettings = userSettings; - _hostsService.FileChanged += (s, e) => - { - _dispatcherQueue.TryEnqueue(() => FileChanged = true); - }; + _hostsService.FileChanged += (s, e) => _dispatcherQueue.TryEnqueue(() => FileChanged = true); + _userSettings.LoopbackDuplicatesChanged += (s, e) => ReadHosts(); } public void Add(Entry entry) @@ -82,12 +98,11 @@ namespace Hosts.ViewModels _entries.Add(entry); FindDuplicates(entry.Address, entry.SplittedHosts); - OnPropertyChanged(nameof(Entries)); } public void Update(int index, Entry entry) { - var existingEntry = Entries.ElementAt(index); + var existingEntry = Entries[index] as Entry; var oldAddress = existingEntry.Address; var oldHosts = existingEntry.SplittedHosts; @@ -98,7 +113,6 @@ namespace Hosts.ViewModels FindDuplicates(oldAddress, oldHosts); FindDuplicates(entry.Address, entry.SplittedHosts); - OnPropertyChanged(nameof(Entries)); } public void DeleteSelected() @@ -108,7 +122,6 @@ namespace Hosts.ViewModels _entries.Remove(Selected); FindDuplicates(address, hosts); - OnPropertyChanged(nameof(Entries)); } public void UpdateAdditionalLines(string lines) @@ -122,12 +135,39 @@ namespace Hosts.ViewModels }); } + public void Move(int oldIndex, int newIndex) + { + if (Filtered) + { + return; + } + + // Swap the IDs + var entry1 = _entries[oldIndex]; + var entry2 = _entries[newIndex]; + (entry2.Id, entry1.Id) = (entry1.Id, entry2.Id); + + // Move entries in the UI + _entries.Move(oldIndex, newIndex); + } + + [RelayCommand] public void ReadHosts() { - FileChanged = false; + if (_readingHosts) + { + return; + } + + _dispatcherQueue.TryEnqueue(() => + { + FileChanged = false; + IsLoading = true; + }); Task.Run(async () => { + _readingHosts = true; (_additionalLines, var entries) = await _hostsService.ReadAsync(); await _dispatcherQueue.EnqueueAsync(() => @@ -140,32 +180,66 @@ namespace Hosts.ViewModels } _entries.CollectionChanged += Entries_CollectionChanged; + Entries = new AdvancedCollectionView(_entries, true); + Entries.SortDescriptions.Add(new SortDescription(nameof(Entry.Id), SortDirection.Ascending)); + ApplyFilters(); OnPropertyChanged(nameof(Entries)); - FindDuplicates(); + IsLoading = false; }); + _readingHosts = false; + + _tokenSource?.Cancel(); + _tokenSource = new CancellationTokenSource(); + FindDuplicates(_tokenSource.Token); }); } + [RelayCommand] public void ApplyFilters() { - if (_entries == null) + var expressions = new List>>(4); + + if (!string.IsNullOrWhiteSpace(_addressFilter)) { - return; + expressions.Add(e => ((Entry)e).Address.Contains(_addressFilter, StringComparison.OrdinalIgnoreCase)); } - Filtered = !string.IsNullOrWhiteSpace(_addressFilter) - || !string.IsNullOrWhiteSpace(_hostsFilter) - || !string.IsNullOrWhiteSpace(_commentFilter); + if (!string.IsNullOrWhiteSpace(_hostsFilter)) + { + expressions.Add(e => ((Entry)e).Hosts.Contains(_hostsFilter, StringComparison.OrdinalIgnoreCase)); + } - OnPropertyChanged(nameof(Entries)); + if (!string.IsNullOrWhiteSpace(_commentFilter)) + { + expressions.Add(e => ((Entry)e).Comment.Contains(_commentFilter, StringComparison.OrdinalIgnoreCase)); + } + + if (_showOnlyDuplicates) + { + expressions.Add(e => ((Entry)e).Duplicate); + } + + Expression> filterExpression = null; + + foreach (var e in expressions) + { + filterExpression = filterExpression == null ? e : filterExpression.And(e); + } + + Filtered = filterExpression != null; + Entries.Filter = Filtered ? filterExpression.Compile().Invoke : null; + Entries.RefreshFilter(); } + [RelayCommand] public void ClearFilters() { AddressFilter = null; HostsFilter = null; CommentFilter = null; ShowOnlyDuplicates = false; + Entries.Filter = null; + Entries.RefreshFilter(); } public async Task PingSelectedAsync() @@ -177,11 +251,13 @@ namespace Hosts.ViewModels selected.Pinging = false; } + [RelayCommand] public void OpenSettings() { SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.Hosts); } + [RelayCommand] public void OpenHostsFile() { _hostsService.OpenHostsFile(); @@ -195,6 +271,14 @@ namespace Hosts.ViewModels private void Entry_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { + if (Filtered && (e.PropertyName == nameof(Entry.Hosts) + || e.PropertyName == nameof(Entry.Address) + || e.PropertyName == nameof(Entry.Comment) + || e.PropertyName == nameof(Entry.Duplicate))) + { + Entries.RefreshFilter(); + } + // Ping and duplicate should't trigger a file save if (e.PropertyName == nameof(Entry.Ping) || e.PropertyName == nameof(Entry.Pinging) @@ -219,19 +303,34 @@ namespace Hosts.ViewModels }); } - private void FindDuplicates() + private void FindDuplicates(CancellationToken cancellationToken) { foreach (var entry in _entries) { - SetDuplicate(entry); + try + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!_userSettings.LoopbackDuplicates && _loopbackAddresses.Contains(entry.Address)) + { + continue; + } + + SetDuplicate(entry); + } + catch (OperationCanceledException) + { + Logger.LogInfo("FindDuplicates cancelled"); + return; + } } } private void FindDuplicates(string address, IEnumerable hosts) { var entries = _entries.Where(e => - string.Equals(e.Address, address, StringComparison.InvariantCultureIgnoreCase) - || hosts.Intersect(e.SplittedHosts, StringComparer.InvariantCultureIgnoreCase).Any()); + string.Equals(e.Address, address, StringComparison.OrdinalIgnoreCase) + || hosts.Intersect(e.SplittedHosts, StringComparer.OrdinalIgnoreCase).Any()); foreach (var entry in entries) { @@ -241,44 +340,27 @@ namespace Hosts.ViewModels private void SetDuplicate(Entry entry) { + if (!_userSettings.LoopbackDuplicates && _loopbackAddresses.Contains(entry.Address)) + { + _dispatcherQueue.TryEnqueue(() => + { + entry.Duplicate = false; + }); + + return; + } + var hosts = entry.SplittedHosts; - entry.Duplicate = _entries.FirstOrDefault(e => - e != entry - && (string.Equals(e.Address, entry.Address, StringComparison.InvariantCultureIgnoreCase) - || hosts.Intersect(e.SplittedHosts, StringComparer.InvariantCultureIgnoreCase).Any())) != null; - } + var duplicate = _entries.FirstOrDefault(e => e != entry + && e.Type == entry.Type + && (string.Equals(e.Address, entry.Address, StringComparison.OrdinalIgnoreCase) + || hosts.Intersect(e.SplittedHosts, StringComparer.OrdinalIgnoreCase).Any())) != null; - private ObservableCollection GetFilteredEntries() - { - if (_entries == null) + _dispatcherQueue.TryEnqueue(() => { - return new ObservableCollection(); - } - - var filter = _entries.AsEnumerable(); - - if (!string.IsNullOrWhiteSpace(_addressFilter)) - { - filter = filter.Where(e => e.Address.Contains(_addressFilter, StringComparison.OrdinalIgnoreCase)); - } - - if (!string.IsNullOrWhiteSpace(_hostsFilter)) - { - filter = filter.Where(e => e.Hosts.Contains(_hostsFilter, StringComparison.OrdinalIgnoreCase)); - } - - if (!string.IsNullOrWhiteSpace(_commentFilter)) - { - filter = filter.Where(e => e.Comment.Contains(_commentFilter, StringComparison.OrdinalIgnoreCase)); - } - - if (_showOnlyDuplicates) - { - filter = filter.Where(e => e.Duplicate); - } - - return new ObservableCollection(filter); + entry.Duplicate = duplicate; + }); } protected virtual void Dispose(bool disposing) diff --git a/src/modules/Hosts/Hosts/Views/MainPage.xaml b/src/modules/Hosts/Hosts/Views/MainPage.xaml index 85aaabaa68..566af4feb5 100644 --- a/src/modules/Hosts/Hosts/Views/MainPage.xaml +++ b/src/modules/Hosts/Hosts/Views/MainPage.xaml @@ -26,6 +26,10 @@ NotEmptyValue="Visible" /> + @@ -194,7 +198,8 @@ IsItemClickEnabled="True" ItemClick="Entries_ItemClick" ItemsSource="{Binding Entries, Mode=TwoWay}" - SelectedItem="{Binding Selected, Mode=TwoWay}"> + SelectedItem="{Binding Selected, Mode=TwoWay}" + Visibility="{x:Bind ViewModel.IsLoading, Converter={StaticResource BoolToInvertedVisibilityConverter}, Mode=OneWay}"> + + 0) { - ViewModel.Entries.Move(index, index - 1); + ViewModel.Move(index, index - 1); } } } @@ -179,7 +179,7 @@ namespace Hosts.Views var index = ViewModel.Entries.IndexOf(entry); if (index < ViewModel.Entries.Count - 1) { - ViewModel.Entries.Move(index, index + 1); + ViewModel.Move(index, index + 1); } } } diff --git a/src/modules/Hosts/Hosts/app.manifest b/src/modules/Hosts/Hosts/app.manifest index 87a882d863..6417c11e2d 100644 --- a/src/modules/Hosts/Hosts/app.manifest +++ b/src/modules/Hosts/Hosts/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/Hosts/HostsModuleInterface/dllmain.cpp b/src/modules/Hosts/HostsModuleInterface/dllmain.cpp index 8daa522b5d..aeb05ba2e6 100644 --- a/src/modules/Hosts/HostsModuleInterface/dllmain.cpp +++ b/src/modules/Hosts/HostsModuleInterface/dllmain.cpp @@ -57,7 +57,7 @@ private: void bring_process_to_front() { auto enum_windows = [](HWND hwnd, LPARAM param) -> BOOL { - HANDLE process_handle = (HANDLE)param; + HANDLE process_handle = reinterpret_cast(param); DWORD window_process_id = 0; GetWindowThreadProcessId(hwnd, &window_process_id); diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 047e418422..93f8a693d9 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,8 +1,8 @@  + - true true @@ -143,21 +143,21 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/app.manifest b/src/modules/MeasureTool/MeasureToolCore/app.manifest index 4aedd37014..1292107c7f 100644 --- a/src/modules/MeasureTool/MeasureToolCore/app.manifest +++ b/src/modules/MeasureTool/MeasureToolCore/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index 9322bb3315..e52f75ad83 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -3,4 +3,5 @@ + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp b/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp index ac80a4a399..4148effdb1 100644 --- a/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp +++ b/src/modules/MeasureTool/MeasureToolModuleInterface/dllmain.cpp @@ -3,9 +3,11 @@ #include #include #include "trace.h" +#include #include #include #include +#include extern "C" IMAGE_DOS_HEADER __ImageBase; @@ -49,6 +51,9 @@ private: Hotkey m_hotkey; HANDLE m_hProcess; + HANDLE triggerEvent; + EventWaiter triggerEventWaiter; + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) { auto settingsObject = settings.get_raw_json(); @@ -142,6 +147,11 @@ public: { LoggerHelpers::init_logger(L"Measure Tool", L"ModuleInterface", "Measure Tool"); init_settings(); + + triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT); + triggerEventWaiter = EventWaiter(CommonSharedConstants::MEASURE_TOOL_TRIGGER_EVENT, [this](int) { + on_hotkey(0); + }); } ~MeasureTool() diff --git a/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs b/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs index f81bc2047f..9cb26f54f5 100644 --- a/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs +++ b/src/modules/MeasureTool/MeasureToolUI/App.xaml.cs @@ -4,7 +4,6 @@ using System; using ManagedCommon; -using MeasureToolUI.Helpers; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; @@ -22,6 +21,8 @@ namespace MeasureToolUI /// public App() { + Logger.InitializeLogger("\\Measure Tool\\MeasureToolUI\\Logs"); + this.InitializeComponent(); } diff --git a/src/modules/MeasureTool/MeasureToolUI/Logger.cs b/src/modules/MeasureTool/MeasureToolUI/Logger.cs deleted file mode 100644 index 30fb1b43a4..0000000000 --- a/src/modules/MeasureTool/MeasureToolUI/Logger.cs +++ /dev/null @@ -1,76 +0,0 @@ -// 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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; - -namespace MeasureToolUI.Helpers -{ - public static class Logger - { - private static readonly string ApplicationLogPath = Path.Combine(interop.Constants.AppDataPath(), "Measure Tool\\MeasureToolUI\\Logs"); - - static Logger() - { - if (!Directory.Exists(ApplicationLogPath)) - { - Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml b/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml index d787abb492..973781a777 100644 --- a/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml +++ b/src/modules/MeasureTool/MeasureToolUI/MainWindow.xaml @@ -5,8 +5,8 @@ xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)" xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:p="using:PowerToys.MeasureToolUI.Properties" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:p="using:PowerToys.MeasureToolUI.Properties" xmlns:winuiex="using:WinUIEx" IsAlwaysOnTop="True" IsMaximizable="False" diff --git a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj index c43cda95ed..96d510e7b9 100644 --- a/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj +++ b/src/modules/MeasureTool/MeasureToolUI/MeasureToolUI.csproj @@ -69,10 +69,10 @@ - - - - + + + + diff --git a/src/modules/MeasureTool/MeasureToolUI/app.manifest b/src/modules/MeasureTool/MeasureToolUI/app.manifest index ba6d50bdd8..908d7af1a8 100644 --- a/src/modules/MeasureTool/MeasureToolUI/app.manifest +++ b/src/modules/MeasureTool/MeasureToolUI/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp index d03e91c684..4c84691095 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp @@ -161,7 +161,7 @@ bool SuperSonar::Initialize(HINSTANCE hinst) wc.hInstance = hinst; wc.hIcon = LoadIcon(hinst, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = className; if (!RegisterClassW(&wc)) @@ -196,14 +196,14 @@ LRESULT SuperSonar::s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM SuperSonar* self; if (message == WM_NCCREATE) { - auto info = (LPCREATESTRUCT)lParam; - SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)info->lpCreateParams); - self = (SuperSonar*)info->lpCreateParams; + auto info = reinterpret_cast(lParam); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(info->lpCreateParams)); + self = static_cast(info->lpCreateParams); self->m_hwnd = hwnd; } else { - self = (SuperSonar*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + self = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); } if (self) { @@ -230,7 +230,7 @@ LRESULT SuperSonar::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n break; case WM_INPUT: - OnSonarInput(wParam, (HRAWINPUT)lParam); + OnSonarInput(wParam, reinterpret_cast(lParam)); break; case WM_TIMER: @@ -272,7 +272,7 @@ void SuperSonar::OnSonarInput(WPARAM flags, HRAWINPUT hInput) RAWINPUT input; UINT size = sizeof(input); auto result = GetRawInputData(hInput, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER)); - if ((int)result < sizeof(RAWINPUTHEADER)) + if (result < sizeof(RAWINPUTHEADER)) { return; } @@ -397,7 +397,7 @@ void SuperSonar::DetectShake() { currentX += movement.diff.x; currentY += movement.diff.y; - distanceTravelled += sqrt((double)movement.diff.x * movement.diff.x + (double)movement.diff.y * movement.diff.y); // Pythagorean theorem + distanceTravelled += sqrt(static_cast(movement.diff.x) * movement.diff.x + static_cast(movement.diff.y) * movement.diff.y); // Pythagorean theorem minX = min(currentX, minX); maxX = max(currentX, maxX); minY = min(currentY, minY); @@ -410,8 +410,8 @@ void SuperSonar::DetectShake() } // Size of the rectangle the pointer moved in. - double rectangleWidth = (double)maxX - minX; - double rectangleHeight = (double)maxY - minY; + double rectangleWidth = static_cast(maxX) - minX; + double rectangleHeight = static_cast(maxY) - minY; double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight); if (diagonal > 0 && distanceTravelled / diagonal > ShakeFactor) @@ -592,7 +592,7 @@ bool SuperSonar::IsForegroundAppExcluded() if (HWND foregroundApp{ GetForegroundWindow() }) { auto processPath = get_process_path(foregroundApp); - CharUpperBuffW(processPath.data(), (DWORD)processPath.length()); + CharUpperBuffW(processPath.data(), static_cast(processPath.length())); return find_app_name_in_path(processPath, m_excludedApps); } else @@ -613,7 +613,7 @@ struct CompositionSpotlight : SuperSonar void AfterMoveSonar() { - m_spotlight.Offset({ (float)m_sonarPos.x, (float)m_sonarPos.y, 0.0f }); + m_spotlight.Offset({ static_cast(m_sonarPos.x), static_cast(m_sonarPos.y), 0.0f }); } LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept @@ -924,10 +924,10 @@ struct GdiSpotlight : GdiSonar auto spotlight = CreateRoundRectRgn( this->m_sonarPos.x - radius, this->m_sonarPos.y - radius, this->m_sonarPos.x + radius, this->m_sonarPos.y + radius, radius * 2, radius * 2); - FillRgn(ps.hdc, spotlight, (HBRUSH)GetStockObject(WHITE_BRUSH)); + FillRgn(ps.hdc, spotlight, static_cast(GetStockObject(WHITE_BRUSH))); Sleep(1000 / 60); ExtSelectClipRgn(ps.hdc, spotlight, RGN_DIFF); - FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)GetStockObject(BLACK_BRUSH)); + FillRect(ps.hdc, &ps.rcPaint, static_cast(GetStockObject(BLACK_BRUSH))); DeleteObject(spotlight); EndPaint(this->m_hwnd, &ps); @@ -959,7 +959,7 @@ struct GdiCrosshairs : GdiSonar auto radius = CurrentSonarRadius(); RECT rc; - HBRUSH white = (HBRUSH)GetStockObject(WHITE_BRUSH); + HBRUSH white = static_cast(GetStockObject(WHITE_BRUSH)); rc.left = m_sonarPos.x - radius; rc.top = ps.rcPaint.top; @@ -973,7 +973,7 @@ struct GdiCrosshairs : GdiSonar rc.bottom = m_sonarPos.y + radius; FillRect(ps.hdc, &rc, white); - HBRUSH black = (HBRUSH)GetStockObject(BLACK_BRUSH); + HBRUSH black = static_cast(GetStockObject(BLACK_BRUSH)); // Top left rc.left = ps.rcPaint.left; diff --git a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp index 27d809d07d..0241a8922b 100644 --- a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp +++ b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp @@ -185,11 +185,16 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Activation Method auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_METHOD); - UINT value = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); - if (value < (int)FindMyMouseActivationMethod::EnumElements) + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value < static_cast(FindMyMouseActivationMethod::EnumElements) && value >= 0) { - findMyMouseSettings.activationMethod = (FindMyMouseActivationMethod)value; + findMyMouseSettings.activationMethod = static_cast(value); } + else + { + throw; + } + } catch (...) { @@ -198,7 +203,7 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) try { auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE); - findMyMouseSettings.doNotActivateOnGameMode = (bool)jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE); + findMyMouseSettings.doNotActivateOnGameMode = jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE); } catch (...) { @@ -246,7 +251,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Overlay Opacity auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_OVERLAY_OPACITY); - findMyMouseSettings.overlayOpacity = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.overlayOpacity = value; + } + else + { + throw; + } } catch (...) { @@ -256,7 +269,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Spotlight Radius auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SPOTLIGHT_RADIUS); - findMyMouseSettings.spotlightRadius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.spotlightRadius = value; + } + else + { + throw; + } } catch (...) { @@ -266,7 +287,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Animation Duration auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ANIMATION_DURATION_MS); - findMyMouseSettings.animationDurationMs = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.animationDurationMs = value; + } + else + { + throw; + } } catch (...) { @@ -276,7 +305,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Spotlight Initial Zoom auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SPOTLIGHT_INITIAL_ZOOM); - findMyMouseSettings.spotlightInitialZoom = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.spotlightInitialZoom = value; + } + else + { + throw; + } } catch (...) { @@ -289,7 +326,7 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) std::wstring apps = jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE).c_str(); std::vector excludedApps; auto excludedUppercase = apps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); @@ -311,7 +348,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { // Parse Shaking Minimum Distance auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHAKING_MINIMUM_DISTANCE); - findMyMouseSettings.shakeMinimumDistance = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.shakeMinimumDistance = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp index f1107db483..2a10ff648d 100644 --- a/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/MouseHighlighter.cpp @@ -132,7 +132,7 @@ void Highlighter::AddDrawingPoint(MouseButton button) auto circleGeometry = m_compositor.CreateEllipseGeometry(); circleGeometry.Radius({ m_radius, m_radius }); auto circleShape = m_compositor.CreateSpriteShape(circleGeometry); - circleShape.Offset({ (float)pt.x, (float)pt.y }); + circleShape.Offset({ static_cast(pt.x), static_cast(pt.y )}); if (button == MouseButton::Left) { circleShape.FillBrush(m_compositor.CreateColorBrush(m_leftClickColor)); @@ -166,12 +166,12 @@ void Highlighter::UpdateDrawingPointPosition(MouseButton button) if (button == MouseButton::Left) { - m_leftPointer.Offset({ (float)pt.x, (float)pt.y }); + m_leftPointer.Offset({ static_cast(pt.x), static_cast(pt.y )}); } else { //right - m_rightPointer.Offset({ (float)pt.x, (float)pt.y }); + m_rightPointer.Offset({ static_cast(pt.x), static_cast(pt.y )}); } } void Highlighter::StartDrawingPointFading(MouseButton button) @@ -217,7 +217,7 @@ LRESULT CALLBACK Highlighter::MouseHookProc(int nCode, WPARAM wParam, LPARAM lPa { if (nCode >= 0) { - MSLLHOOKSTRUCT* hookData = (MSLLHOOKSTRUCT*)lParam; + MSLLHOOKSTRUCT* hookData = reinterpret_cast(lParam); switch (wParam) { case WM_LBUTTONDOWN: @@ -293,7 +293,7 @@ void Highlighter::SwitchActivationMode() } void Highlighter::ApplySettings(MouseHighlighterSettings settings) { - m_radius = (float)settings.radius; + m_radius = static_cast(settings.radius); m_fadeDelay_ms = settings.fadeDelayMs; m_fadeDuration_ms = settings.fadeDurationMs; m_leftClickColor = settings.leftButtonColor; @@ -349,7 +349,7 @@ bool Highlighter::MyRegisterClass(HINSTANCE hInstance) wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = m_className; if (!RegisterClassW(&wc)) diff --git a/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp b/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp index 75cf746a02..b3860a227b 100644 --- a/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/dllmain.cpp @@ -215,7 +215,15 @@ public: { // Parse Opacity auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_OPACITY); - opacity = (uint8_t)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + opacity = value; + } + else + { + throw; + } } catch (...) { @@ -234,7 +242,7 @@ public: auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_LEFT_BUTTON_CLICK_COLOR); auto leftColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE); uint8_t r, g, b; - if (!checkValidRGB(leftColor,&r,&g,&b)) + if (!checkValidRGB(leftColor, &r, &g, &b)) { Logger::error("Left click color RGB value is invalid. Will use default value"); } @@ -270,7 +278,15 @@ public: { // Parse Radius auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_RADIUS); - highlightSettings.radius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + highlightSettings.radius = value; + } + else + { + throw; + } } catch (...) { @@ -280,7 +296,15 @@ public: { // Parse Fade Delay auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_FADE_DELAY_MS); - highlightSettings.fadeDelayMs = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + highlightSettings.fadeDelayMs = value; + } + else + { + throw; + } } catch (...) { @@ -290,7 +314,15 @@ public: { // Parse Fade Duration auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_HIGHLIGHT_FADE_DURATION_MS); - highlightSettings.fadeDurationMs = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + highlightSettings.fadeDurationMs = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/MouseUtils/MouseJump/MouseJump.rc b/src/modules/MouseUtils/MouseJump/MouseJump.rc new file mode 100644 index 0000000000..f8d0a54cda --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/MouseJump.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj b/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj new file mode 100644 index 0000000000..16b9692ffd --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/MouseJump.vcxproj @@ -0,0 +1,129 @@ + + + + + 15.0 + {8a08d663-4995-40e3-b42c-3f910625f284} + Win32Proj + MouseJumpModuleInterface + MouseJump + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\MouseUtils\ + PowerToys.MouseJump + + + true + + + false + + + + Level3 + Disabled + true + _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + MultiThreadedDebug + stdcpplatest + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + MultiThreaded + stdcpplatest + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + + + + + $(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories) + + + + + Use + pch.h + + + + + + + + + + + Create + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJump/dllmain.cpp b/src/modules/MouseUtils/MouseJump/dllmain.cpp new file mode 100644 index 0000000000..4be0abdc48 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/dllmain.cpp @@ -0,0 +1,302 @@ +#include "pch.h" + +#include +//#include +//#include +#include +#include "trace.h" +#include +#include + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +HMODULE m_hModule; + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) +{ + m_hModule = hModule; + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +// The PowerToy name that will be shown in the settings. +const static wchar_t* MODULE_NAME = L"MouseJump"; +// Add a description that will we shown in the module settings page. +const static wchar_t* MODULE_DESC = L"Quickly move the mouse long distances"; + +namespace +{ + const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; + const wchar_t JSON_KEY_WIN[] = L"win"; + const wchar_t JSON_KEY_ALT[] = L"alt"; + const wchar_t JSON_KEY_CTRL[] = L"ctrl"; + const wchar_t JSON_KEY_SHIFT[] = L"shift"; + const wchar_t JSON_KEY_CODE[] = L"code"; + const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut"; +} + +// Implement the PowerToy Module Interface and all the required methods. +class MouseJump : public PowertoyModuleIface +{ +private: + // The PowerToy state. + bool m_enabled = false; + + // Hotkey to invoke the module + Hotkey m_hotkey; + HANDLE m_hProcess; + + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) + { + auto settingsObject = settings.get_raw_json(); + if (settingsObject.GetView().Size()) + { + try + { + auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT); + m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN); + m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT); + m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT); + m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL); + m_hotkey.key = static_cast(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE)); + } + catch (...) + { + Logger::error("Failed to initialize Mouse Jump start shortcut"); + } + } + else + { + Logger::info("MouseJump settings are empty"); + } + + if (!m_hotkey.key) + { + Logger::info("MouseJump is going to use default shortcut"); + m_hotkey.win = true; + m_hotkey.alt = false; + m_hotkey.shift = true; + m_hotkey.ctrl = false; + m_hotkey.key = 'D'; + } + } + + bool is_process_running() + { + return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; + } + + void launch_process() + { + Logger::trace(L"Starting MouseJump process"); + unsigned long powertoys_pid = GetCurrentProcessId(); + + std::wstring executable_args = L""; + executable_args.append(std::to_wstring(powertoys_pid)); + + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = L"modules\\MouseUtils\\MouseJumpUI\\PowerToys.MouseJumpUI.exe"; + sei.nShow = SW_SHOWNORMAL; + sei.lpParameters = executable_args.data(); + if (ShellExecuteExW(&sei)) + { + Logger::trace("Successfully started the Mouse Jump process"); + } + else + { + Logger::error(L"Mouse Jump failed to start. {}", get_last_error_or_default(GetLastError())); + } + + m_hProcess = sei.hProcess; + } + + // Load initial settings from the persisted values. + void init_settings(); + + void terminate_process() + { + TerminateProcess(m_hProcess, 1); + } + +public: + // Constructor + MouseJump() + { + LoggerHelpers::init_logger(MODULE_NAME, L"ModuleInterface", LogSettings::mouseJumpLoggerName); + init_settings(); + }; + + ~MouseJump() + { + if (m_enabled) + { + terminate_process(); + } + m_enabled = false; + } + + // Destroy the powertoy and free memory + virtual void destroy() override + { + delete this; + } + + // Return the display name of the powertoy, this will be cached by the runner + virtual const wchar_t* get_name() override + { + return MODULE_NAME; + } + + // Return the non localized key of the powertoy, this will be cached by the runner + virtual const wchar_t* get_key() override + { + return MODULE_NAME; + } + + // Return the configured status for the gpo policy for the module + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredMouseJumpEnabledValue(); + } + + // Return JSON with the configuration options. + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(MODULE_DESC); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + // Signal from the Settings editor to call a custom action. + // This can be used to spawn more complex editors. + virtual void call_custom_action(const wchar_t* action) override + { + } + + // Called by the runner to pass the updated settings values as a serialized JSON. + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + parse_hotkey(values); + + values.save_to_settings_file(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + // Enable the powertoy + virtual void enable() + { + m_enabled = true; + Trace::EnableJumpTool(true); + } + + // Disable the powertoy + virtual void disable() + { + if (m_enabled) + { + terminate_process(); + } + + m_enabled = false; + Trace::EnableJumpTool(false); + } + + // Returns if the powertoys is enabled + virtual bool is_enabled() override + { + return m_enabled; + } + + virtual bool on_hotkey(size_t /*hotkeyId*/) override + { + if (m_enabled) + { + Logger::trace(L"MouseJump hotkey pressed"); + Trace::InvokeJumpTool(); + if (is_process_running()) + { + terminate_process(); + } + launch_process(); + + return true; + } + + return false; + } + + virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override + { + if (m_hotkey.key) + { + if (hotkeys && buffer_size >= 1) + { + hotkeys[0] = m_hotkey; + } + + return 1; + } + else + { + return 0; + } + } + + // Returns whether the PowerToys should be enabled by default + virtual bool is_enabled_by_default() const override + { + return false; + } + +}; + +// Load the settings file. +void MouseJump::init_settings() +{ + try + { + // Load and parse the settings file for this PowerToy. + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(MouseJump::get_name()); + + parse_hotkey(settings); + + } + catch (std::exception&) + { + Logger::warn(L"An exception occurred while loading the settings file"); + // Error while loading from the settings file. Let default values stay as they are. + } +} + + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new MouseJump(); +} \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJump/packages.config b/src/modules/MouseUtils/MouseJump/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJump/pch.cpp b/src/modules/MouseUtils/MouseJump/pch.cpp new file mode 100644 index 0000000000..1d9f38c57d --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/modules/MouseUtils/MouseJump/pch.h b/src/modules/MouseUtils/MouseJump/pch.h new file mode 100644 index 0000000000..74abb62da1 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/pch.h @@ -0,0 +1,10 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include + +//#include +#include +#include +#include diff --git a/src/modules/MouseUtils/MouseJump/resource.h b/src/modules/MouseUtils/MouseJump/resource.h new file mode 100644 index 0000000000..67b823fd7f --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by MeasureTool.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys MouseJump" +#define INTERNAL_NAME "PowerToys.MouseJump" +#define ORIGINAL_FILENAME "PowerToys.MouseJump.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/MouseUtils/MouseJump/trace.cpp b/src/modules/MouseUtils/MouseJump/trace.cpp new file mode 100644 index 0000000000..9f5380284d --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/trace.cpp @@ -0,0 +1,38 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +void Trace::EnableJumpTool(const bool enabled) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "MouseJump_EnableJumpTool", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingBoolean(enabled, "Enabled")); +} + +void Trace::InvokeJumpTool() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "MouseJump_InvokeJumpTool", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} diff --git a/src/modules/MouseUtils/MouseJump/trace.h b/src/modules/MouseUtils/MouseJump/trace.h new file mode 100644 index 0000000000..aaaa336291 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump/trace.h @@ -0,0 +1,12 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + + static void EnableJumpTool(const bool enabled) noexcept; + + static void InvokeJumpTool() noexcept; +}; diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs new file mode 100644 index 0000000000..7579780ebc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/DrawingHelperTests.cs @@ -0,0 +1,229 @@ +// 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. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Helpers; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; +using MouseJumpUI.Models.Screen; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.UnitTests.Helpers; + +[TestClass] +public static class DrawingHelperTests +{ + [TestClass] + public sealed class CalculateLayoutInfoTests + { + public sealed class TestCase + { + public TestCase(LayoutConfig layoutConfig, LayoutInfo expectedResult) + { + this.LayoutConfig = layoutConfig; + this.ExpectedResult = expectedResult; + } + + public LayoutConfig LayoutConfig { get; set; } + + public LayoutInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // happy path - check the preview form is shown + // at the correct size and position on a single screen + // + // +----------------+ + // | | + // | 0 | + // | | + // +----------------+ + var layoutConfig = new LayoutConfig( + virtualScreenBounds: new(0, 0, 5120, 1440), + screens: new List + { + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 5120, 1440), new(0, 0, 5120, 1440)), + }, + activatedLocation: new(5120 / 2, 1440 / 2), + activatedScreenIndex: 0, + activatedScreenNumber: 1, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + var layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new(1760, 491.40625M, 1600, 457.1875M), + previewBounds: new(0, 0, 1590, 447.1875M), + screenBounds: new List + { + new(0, 0, 1590, 447.1875M), + }, + activatedScreenBounds: new(0, 0, 5120, 1440)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + + // primary monitor not topmost / leftmost - if there are screens + // that are further left or higher than the primary monitor + // they'll have negative coordinates which has caused some + // issues with calculations in the past. this test will make + // sure we handle negative coordinates gracefully + // + // +-------+ + // | 0 +----------------+ + // +-------+ | + // | 1 | + // | | + // +----------------+ + layoutConfig = new LayoutConfig( + virtualScreenBounds: new(-1920, -472, 7040, 1912), + screens: new List + { + new ScreenInfo(HMONITOR.Null, false, new(-1920, -472, 1920, 1080), new(-1920, -472, 1920, 1080)), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 5120, 1440), new(0, 0, 5120, 1440)), + }, + activatedLocation: new(-960, -236), + activatedScreenIndex: 0, + activatedScreenNumber: 1, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new( + -1760, + -456.91477M, // -236 - (((decimal)(1600-10) / 7040 * 1912) + 10) / 2 + 1600, + 441.829545M // ((decimal)(1600-10) / 7040 * 1912) + 10 + ), + previewBounds: new(0, 0, 1590, 431.829545M), + screenBounds: new List + { + new(0, 0, 433.63636M, 243.92045M), + new(433.63636M, 106.602270M, 1156.36363M, 325.22727M), + }, + activatedScreenBounds: new(-1920, -472, 1920, 1080)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + + // check we handle rounding errors in scaling the preview form + // that might make the form *larger* than the current screen - + // e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768 + // with a 5px form padding border: + // + // ((decimal)1014 / 7168) * 7168 = 1014.0000000000000000000000002 + // + // +----------------+ + // | | + // | 1 +-------+ + // | | 0 | + // +----------------+-------+ + layoutConfig = new LayoutConfig( + virtualScreenBounds: new(0, 0, 7168, 1440), + screens: new List + { + new ScreenInfo(HMONITOR.Null, false, new(6144, 0, 1024, 768), new(6144, 0, 1024, 768)), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 6144, 1440), new(0, 0, 6144, 1440)), + }, + activatedLocation: new(6656, 384), + activatedScreenIndex: 0, + activatedScreenNumber: 1, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new(6144, 277.14732M, 1024, 213.70535M), + previewBounds: new(0, 0, 1014, 203.70535M), + screenBounds: new List + { + new(869.14285M, 0, 144.85714M, 108.642857M), + new(0, 0, 869.142857M, 203.705357M), + }, + activatedScreenBounds: new(6144, 0, 1024, 768)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + + // check we handle rounding errors in scaling the preview form + // that might make the form a pixel *smaller* than the current screen - + // e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768 + // with a 5px form padding border: + // + // ((decimal)1280 / 7424) * 7424 = 1279.9999999999999999999999999 + // + // +----------------+ + // | | + // | 1 +-------+ + // | | 0 | + // +----------------+-------+ + layoutConfig = new LayoutConfig( + virtualScreenBounds: new(0, 0, 7424, 1440), + screens: new List + { + new ScreenInfo(HMONITOR.Null, false, new(6144, 0, 1280, 768), new(6144, 0, 1280, 768)), + new ScreenInfo(HMONITOR.Null, false, new(0, 0, 6144, 1440), new(0, 0, 6144, 1440)), + }, + activatedLocation: new(6784, 384), + activatedScreenIndex: 0, + activatedScreenNumber: 1, + maximumFormSize: new(1600, 1200), + formPadding: new(5, 5, 5, 5), + previewPadding: new(0, 0, 0, 0)); + layoutInfo = new LayoutInfo( + layoutConfig: layoutConfig, + formBounds: new( + 6144, + 255.83189M, // (768 - (((decimal)(1280-10) / 7424 * 1440) + 10)) / 2 + 1280, + 256.33620M // ((decimal)(1280 - 10) / 7424 * 1440) + 10 + ), + previewBounds: new(0, 0, 1270, 246.33620M), + screenBounds: new List + { + new(1051.03448M, 0, 218.96551M, 131.37931M), + new(0, 0M, 1051.03448M, 246.33620M), + }, + activatedScreenBounds: new(6144, 0, 1280, 768)); + yield return new[] { new TestCase(layoutConfig, layoutInfo) }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + // note - even if values are within 0.0001M of each other they could + // still round to different values - e.g. + // (int)1279.999999999999 -> 1279 + // vs + // (int)1280.000000000000 -> 1280 + // so we'll compare the raw values, *and* convert to an int-based + // Rectangle to compare rounded values + var actual = LayoutHelper.CalculateLayoutInfo(data.LayoutConfig); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.FormBounds.X, actual.FormBounds.X, 0.00001M, "FormBounds.X"); + Assert.AreEqual(expected.FormBounds.Y, actual.FormBounds.Y, 0.00001M, "FormBounds.Y"); + Assert.AreEqual(expected.FormBounds.Width, actual.FormBounds.Width, 0.00001M, "FormBounds.Width"); + Assert.AreEqual(expected.FormBounds.Height, actual.FormBounds.Height, 0.00001M, "FormBounds.Height"); + Assert.AreEqual(expected.FormBounds.ToRectangle(), actual.FormBounds.ToRectangle(), "FormBounds.ToRectangle"); + Assert.AreEqual(expected.PreviewBounds.X, actual.PreviewBounds.X, 0.00001M, "PreviewBounds.X"); + Assert.AreEqual(expected.PreviewBounds.Y, actual.PreviewBounds.Y, 0.00001M, "PreviewBounds.Y"); + Assert.AreEqual(expected.PreviewBounds.Width, actual.PreviewBounds.Width, 0.00001M, "PreviewBounds.Width"); + Assert.AreEqual(expected.PreviewBounds.Height, actual.PreviewBounds.Height, 0.00001M, "PreviewBounds.Height"); + Assert.AreEqual(expected.PreviewBounds.ToRectangle(), actual.PreviewBounds.ToRectangle(), "PreviewBounds.ToRectangle"); + Assert.AreEqual(expected.ScreenBounds.Count, actual.ScreenBounds.Count, "ScreenBounds.Count"); + for (var i = 0; i < expected.ScreenBounds.Count; i++) + { + Assert.AreEqual(expected.ScreenBounds[i].X, actual.ScreenBounds[i].X, 0.00001M, $"ScreenBounds[{i}].X"); + Assert.AreEqual(expected.ScreenBounds[i].Y, actual.ScreenBounds[i].Y, 0.00001M, $"ScreenBounds[{i}].Y"); + Assert.AreEqual(expected.ScreenBounds[i].Width, actual.ScreenBounds[i].Width, 0.00001M, $"ScreenBounds[{i}].Width"); + Assert.AreEqual(expected.ScreenBounds[i].Height, actual.ScreenBounds[i].Height, 0.00001M, $"ScreenBounds[{i}].Height"); + Assert.AreEqual(expected.ScreenBounds[i].ToRectangle(), actual.ScreenBounds[i].ToRectangle(), "ActivatedScreen.ToRectangle"); + } + + Assert.AreEqual(expected.ActivatedScreenBounds.X, actual.ActivatedScreenBounds.X, "ActivatedScreen.X"); + Assert.AreEqual(expected.ActivatedScreenBounds.Y, actual.ActivatedScreenBounds.Y, "ActivatedScreen.Y"); + Assert.AreEqual(expected.ActivatedScreenBounds.Width, actual.ActivatedScreenBounds.Width, "ActivatedScreen.Width"); + Assert.AreEqual(expected.ActivatedScreenBounds.Height, actual.ActivatedScreenBounds.Height, "ActivatedScreen.Height"); + Assert.AreEqual(expected.ActivatedScreenBounds.ToRectangle(), actual.ActivatedScreenBounds.ToRectangle(), "ActivatedScreen.ToRectangle"); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs new file mode 100644 index 0000000000..e0267d4787 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Helpers/MouseHelperTests.cs @@ -0,0 +1,74 @@ +// 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. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Helpers; +using MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.UnitTests.Helpers; + +[TestClass] +public static class MouseHelperTests +{ + [TestClass] + public sealed class GetJumpLocationTests + { + public sealed class TestCase + { + public TestCase(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds, PointInfo expectedResult) + { + this.PreviewLocation = previewLocation; + this.PreviewSize = previewSize; + this.DesktopBounds = desktopBounds; + this.ExpectedResult = expectedResult; + } + + public PointInfo PreviewLocation { get; set; } + + public SizeInfo PreviewSize { get; set; } + + public RectangleInfo DesktopBounds { get; set; } + + public PointInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // screen corners and midpoint with a zero origin + yield return new[] { new TestCase(new(0, 0), new(160, 120), new(0, 0, 1600, 1200), new(0, 0)) }; + yield return new[] { new TestCase(new(160, 0), new(160, 120), new(0, 0, 1600, 1200), new(1600, 0)) }; + yield return new[] { new TestCase(new(0, 120), new(160, 120), new(0, 0, 1600, 1200), new(0, 1200)) }; + yield return new[] { new TestCase(new(160, 120), new(160, 120), new(0, 0, 1600, 1200), new(1600, 1200)) }; + yield return new[] { new TestCase(new(80, 60), new(160, 120), new(0, 0, 1600, 1200), new(800, 600)) }; + + // screen corners and midpoint with a positive origin + yield return new[] { new TestCase(new(0, 0), new(160, 120), new(1000, 1000, 1600, 1200), new(1000, 1000)) }; + yield return new[] { new TestCase(new(160, 0), new(160, 120), new(1000, 1000, 1600, 1200), new(2600, 1000)) }; + yield return new[] { new TestCase(new(0, 120), new(160, 120), new(1000, 1000, 1600, 1200), new(1000, 2200)) }; + yield return new[] { new TestCase(new(160, 120), new(160, 120), new(1000, 1000, 1600, 1200), new(2600, 2200)) }; + yield return new[] { new TestCase(new(80, 60), new(160, 120), new(1000, 1000, 1600, 1200), new(1800, 1600)) }; + + // screen corners and midpoint with a negative origin + yield return new[] { new TestCase(new(0, 0), new(160, 120), new(-1000, -1000, 1600, 1200), new(-1000, -1000)) }; + yield return new[] { new TestCase(new(160, 0), new(160, 120), new(-1000, -1000, 1600, 1200), new(600, -1000)) }; + yield return new[] { new TestCase(new(0, 120), new(160, 120), new(-1000, -1000, 1600, 1200), new(-1000, 200)) }; + yield return new[] { new TestCase(new(160, 120), new(160, 120), new(-1000, -1000, 1600, 1200), new(600, 200)) }; + yield return new[] { new TestCase(new(80, 60), new(160, 120), new(-1000, -1000, 1600, 1200), new(-200, -400)) }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = MouseHelper.GetJumpLocation( + data.PreviewLocation, + data.PreviewSize, + data.DesktopBounds); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.X, actual.X); + Assert.AreEqual(expected.Y, actual.Y); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/RectangleInfoTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/RectangleInfoTests.cs new file mode 100644 index 0000000000..24d3bac4e1 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/RectangleInfoTests.cs @@ -0,0 +1,141 @@ +// 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. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.UnitTests.Models.Drawing; + +[TestClass] +public static class RectangleInfoTests +{ + [TestClass] + public sealed class CenterTests + { + public sealed class TestCase + { + public TestCase(RectangleInfo rectangle, PointInfo point, RectangleInfo expectedResult) + { + this.Rectangle = rectangle; + this.Point = point; + this.ExpectedResult = expectedResult; + } + + public RectangleInfo Rectangle { get; set; } + + public PointInfo Point { get; set; } + + public RectangleInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // zero-sized + yield return new[] { new TestCase(new(0, 0, 0, 0), new(0, 0), new(0, 0, 0, 0)), }; + + // zero-origin + yield return new[] { new TestCase(new(0, 0, 200, 200), new(100, 100), new(0, 0, 200, 200)), }; + yield return new[] { new TestCase(new(0, 0, 200, 200), new(500, 500), new(400, 400, 200, 200)), }; + yield return new[] { new TestCase(new(0, 0, 800, 600), new(1000, 1000), new(600, 700, 800, 600)), }; + + // non-zero origin + yield return new[] { new TestCase(new(1000, 2000, 200, 200), new(100, 100), new(0, 0, 200, 200)), }; + yield return new[] { new TestCase(new(1000, 2000, 200, 200), new(500, 500), new(400, 400, 200, 200)), }; + yield return new[] { new TestCase(new(1000, 2000, 800, 600), new(1000, 1000), new(600, 700, 800, 600)), }; + + // negative result + yield return new[] { new TestCase(new(0, 0, 1000, 1200), new(300, 300), new(-200, -300, 1000, 1200)), }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Rectangle.Center(data.Point); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.X, actual.X); + Assert.AreEqual(expected.Y, actual.Y); + Assert.AreEqual(expected.Width, actual.Width); + Assert.AreEqual(expected.Height, actual.Height); + } + } + + [TestClass] + public sealed class ClampTests + { + public sealed class TestCase + { + public TestCase(RectangleInfo inner, RectangleInfo outer, RectangleInfo expectedResult) + { + this.Inner = inner; + this.Outer = outer; + this.ExpectedResult = expectedResult; + } + + public RectangleInfo Inner { get; set; } + + public RectangleInfo Outer { get; set; } + + public RectangleInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // already inside - obj fills bounds exactly + yield return new[] + { + new TestCase(new(0, 0, 100, 100), new(0, 0, 100, 100), new(0, 0, 100, 100)), + }; + + // already inside - obj exactly in each corner + yield return new[] + { + new TestCase(new(0, 0, 100, 100), new(0, 0, 200, 200), new(0, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(100, 0, 100, 100), new(0, 0, 200, 200), new(100, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(0, 100, 100, 100), new(0, 0, 200, 200), new(0, 100, 100, 100)), + }; + yield return new[] + { + new TestCase(new(100, 100, 100, 100), new(0, 0, 200, 200), new(100, 100, 100, 100)), + }; + + // move inside - obj outside each corner + yield return new[] + { + new TestCase(new(-50, -50, 100, 100), new(0, 0, 200, 200), new(0, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(250, -50, 100, 100), new(0, 0, 200, 200), new(100, 0, 100, 100)), + }; + yield return new[] + { + new TestCase(new(-50, 250, 100, 100), new(0, 0, 200, 200), new(0, 100, 100, 100)), + }; + yield return new[] + { + new TestCase(new(150, 150, 100, 100), new(0, 0, 200, 200), new(100, 100, 100, 100)), + }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Inner.Clamp(data.Outer); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.X, actual.X); + Assert.AreEqual(expected.Y, actual.Y); + Assert.AreEqual(expected.Width, actual.Width); + Assert.AreEqual(expected.Height, actual.Height); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/SizeInfoTests.cs b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/SizeInfoTests.cs new file mode 100644 index 0000000000..5e0bd3d4fe --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/Models/Drawing/SizeInfoTests.cs @@ -0,0 +1,106 @@ +// 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. + +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.UnitTests.Drawing; + +[TestClass] +public static class SizeInfoTests +{ + [TestClass] + public sealed class ScaleToFitTests + { + public sealed class TestCase + { + public TestCase(SizeInfo obj, SizeInfo bounds, SizeInfo expectedResult) + { + this.Obj = obj; + this.Bounds = bounds; + this.ExpectedResult = expectedResult; + } + + public SizeInfo Obj { get; set; } + + public SizeInfo Bounds { get; set; } + + public SizeInfo ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // identity tests + yield return new[] { new TestCase(new(512, 384), new(512, 384), new(512, 384)), }; + yield return new[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768)), }; + + // general tests + yield return new[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536)), }; + yield return new[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768)), }; + + // scale to fit width + yield return new[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536)), }; + + // scale to fit height + yield return new[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536)), }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Obj.ScaleToFit(data.Bounds); + var expected = data.ExpectedResult; + Assert.AreEqual(expected.Width, actual.Width); + Assert.AreEqual(expected.Height, actual.Height); + } + } + + [TestClass] + public sealed class ScaleToFitRatioTests + { + public sealed class TestCase + { + public TestCase(SizeInfo obj, SizeInfo bounds, decimal expectedResult) + { + this.Obj = obj; + this.Bounds = bounds; + this.ExpectedResult = expectedResult; + } + + public SizeInfo Obj { get; set; } + + public SizeInfo Bounds { get; set; } + + public decimal ExpectedResult { get; set; } + } + + public static IEnumerable GetTestCases() + { + // identity tests + yield return new[] { new TestCase(new(512, 384), new(512, 384), 1), }; + yield return new[] { new TestCase(new(1024, 768), new(1024, 768), 1), }; + + // general tests + yield return new[] { new TestCase(new(512, 384), new(2048, 1536), 4), }; + yield return new[] { new TestCase(new(2048, 1536), new(1024, 768), 0.5M), }; + + // scale to fit width + yield return new[] { new TestCase(new(512, 384), new(2048, 3072), 4), }; + + // scale to fit height + yield return new[] { new TestCase(new(512, 384), new(4096, 1536), 4), }; + } + + [TestMethod] + [DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)] + public void RunTestCases(TestCase data) + { + var actual = data.Obj.ScaleToFitRatio(data.Bounds); + var expected = data.ExpectedResult; + Assert.AreEqual(expected, actual); + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj new file mode 100644 index 0000000000..67886dc272 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj @@ -0,0 +1,29 @@ + + + + + net7.0-windows10.0.19041.0 + {D9C5DE64-6849-4278-91AD-9660AECF2876} + Microsoft.MouseJumpUI.UnitTests + false + enable + Library + $(Version).0 + + + + ..\..\..\..\$(Platform)\$(Configuration)\modules\MouseUtils\MouseJumpUI.UnitTests\ + + + + + + + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs new file mode 100644 index 0000000000..225ed68b2d --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/DrawingHelper.cs @@ -0,0 +1,184 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.Helpers; + +internal static class DrawingHelper +{ + /// + /// Draw the gradient-filled preview background. + /// + public static void DrawPreviewBackground( + Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable screenBounds) + { + using var backgroundBrush = new LinearGradientBrush( + previewBounds.Location.ToPoint(), + previewBounds.Size.ToPoint(), + Color.FromArgb(13, 87, 210), // light blue + Color.FromArgb(3, 68, 192)); // darker blue + + // it's faster to build a region with the screen areas excluded + // and fill that than it is to fill the entire bounding rectangle + var backgroundRegion = new Region(previewBounds.ToRectangle()); + foreach (var screen in screenBounds) + { + backgroundRegion.Exclude(screen.ToRectangle()); + } + + previewGraphics.FillRegion(backgroundBrush, backgroundRegion); + } + + public static void EnsureDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc) + { + if (desktopHwnd.IsNull) + { + desktopHwnd = User32.GetDesktopWindow(); + } + + if (desktopHdc.IsNull) + { + desktopHdc = User32.GetWindowDC(desktopHwnd); + if (desktopHdc.IsNull) + { + throw new InvalidOperationException( + $"{nameof(User32.GetWindowDC)} returned null"); + } + } + } + + public static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc) + { + if (!desktopHwnd.IsNull && !desktopHdc.IsNull) + { + var result = User32.ReleaseDC(desktopHwnd, desktopHdc); + if (result == 0) + { + throw new InvalidOperationException( + $"{nameof(User32.ReleaseDC)} returned {result}"); + } + } + + desktopHwnd = HWND.Null; + desktopHdc = HDC.Null; + } + + /// + /// Checks if the device context handle exists, and creates a new one from the + /// specified Graphics object if not. + /// + public static void EnsurePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc) + { + if (previewHdc.IsNull) + { + previewHdc = new HDC(previewGraphics.GetHdc()); + var result = Gdi32.SetStretchBltMode(previewHdc, Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE); + + if (result == 0) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.SetStretchBltMode)} returned {result}"); + } + } + } + + /// + /// Free the specified device context handle if it exists. + /// + public static void FreePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc) + { + if ((previewGraphics is not null) && !previewHdc.IsNull) + { + previewGraphics.ReleaseHdc(previewHdc.Value); + previewHdc = HDC.Null; + } + } + + /// + /// Draw placeholder images for any non-activated screens on the preview. + /// Will release the specified device context handle if it needs to draw anything. + /// + public static void DrawPreviewScreenPlaceholders( + Graphics previewGraphics, IEnumerable screenBounds) + { + // we can exclude the activated screen because we've already draw + // the screen capture image for that one on the preview + if (screenBounds.Any()) + { + var brush = Brushes.Black; + previewGraphics.FillRectangles(brush, screenBounds.Select(screen => screen.ToRectangle()).ToArray()); + } + } + + /// + /// Draws a screen capture from the specified desktop handle onto the target device context. + /// + public static void DrawPreviewScreen( + HDC sourceHdc, + HDC targetHdc, + RectangleInfo sourceBounds, + RectangleInfo targetBounds) + { + var source = sourceBounds.ToRectangle(); + var target = targetBounds.ToRectangle(); + var result = Gdi32.StretchBlt( + targetHdc, + target.X, + target.Y, + target.Width, + target.Height, + sourceHdc, + source.X, + source.Y, + source.Width, + source.Height, + Gdi32.ROP_CODE.SRCCOPY); + if (!result) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } + } + + /// + /// Draws screen captures from the specified desktop handle onto the target device context. + /// + public static void DrawPreviewScreens( + HDC sourceHdc, + HDC targetHdc, + IList sourceBounds, + IList targetBounds) + { + for (var i = 0; i < sourceBounds.Count; i++) + { + var source = sourceBounds[i].ToRectangle(); + var target = targetBounds[i].ToRectangle(); + var result = Gdi32.StretchBlt( + targetHdc, + target.X, + target.Y, + target.Width, + target.Height, + sourceHdc, + source.X, + source.Y, + source.Width, + source.Height, + Gdi32.ROP_CODE.SRCCOPY); + if (!result) + { + throw new InvalidOperationException( + $"{nameof(Gdi32.StretchBlt)} returned {result.Value}"); + } + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs new file mode 100644 index 0000000000..768a6f8ddd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/LayoutHelper.cs @@ -0,0 +1,92 @@ +// 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. + +using System; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; + +namespace MouseJumpUI.Helpers; + +internal static class LayoutHelper +{ + public static LayoutInfo CalculateLayoutInfo( + LayoutConfig layoutConfig) + { + if (layoutConfig is null) + { + throw new ArgumentNullException(nameof(layoutConfig)); + } + + var builder = new LayoutInfo.Builder + { + LayoutConfig = layoutConfig, + }; + + builder.ActivatedScreenBounds = layoutConfig.Screens[layoutConfig.ActivatedScreenIndex].Bounds; + + // work out the maximum *constrained* form size + // * can't be bigger than the activated screen + // * can't be bigger than the max form size + var maxFormSize = builder.ActivatedScreenBounds.Size + .Intersect(layoutConfig.MaximumFormSize); + + // the drawing area for screen images is inside the + // form border and inside the preview border + var maxDrawingSize = maxFormSize + .Shrink(layoutConfig.FormPadding) + .Shrink(layoutConfig.PreviewPadding); + + // scale the virtual screen to fit inside the drawing bounds + var scalingRatio = layoutConfig.VirtualScreenBounds.Size + .ScaleToFitRatio(maxDrawingSize); + + // position the drawing bounds inside the preview border + var drawingBounds = layoutConfig.VirtualScreenBounds.Size + .ScaleToFit(maxDrawingSize) + .PlaceAt(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top); + + // now we know the size of the drawing area we can work out the preview size + builder.PreviewBounds = drawingBounds.Enlarge(layoutConfig.PreviewPadding); + + // ... and the form size + // * center the form to the activated position, but nudge it back + // inside the visible area of the activated screen if it falls outside + builder.FormBounds = builder.PreviewBounds + .Enlarge(layoutConfig.FormPadding) + .Center(layoutConfig.ActivatedLocation) + .Clamp(builder.ActivatedScreenBounds); + + // now calculate the positions of each of the screen images on the preview + builder.ScreenBounds = layoutConfig.Screens + .Select( + screen => screen.Bounds + .Offset(layoutConfig.VirtualScreenBounds.Location.ToSize().Negate()) + .Scale(scalingRatio) + .Offset(layoutConfig.PreviewPadding.Left, layoutConfig.PreviewPadding.Top)) + .ToList(); + + return builder.Build(); + } + + /// + /// Resize and position the specified form. + /// + public static void PositionForm( + Form form, RectangleInfo formBounds) + { + // note - do this in two steps rather than "this.Bounds = formBounds" as there + // appears to be an issue in WinForms with dpi scaling even when using PerMonitorV2, + // where the form scaling uses either the *primary* screen scaling or the *previous* + // screen's scaling when the form is moved to a different screen. i've got no idea + // *why*, but the exact sequence of calls below seems to be a workaround... + // see https://github.com/mikeclayton/FancyMouse/issues/2 + var bounds = formBounds.ToRectangle(); + form.Location = bounds.Location; + _ = form.PointToScreen(Point.Empty); + form.Size = bounds.Size; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs new file mode 100644 index 0000000000..78eb9a496d --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/MouseHelper.cs @@ -0,0 +1,137 @@ +// 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. + +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.Helpers; + +internal static class MouseHelper +{ + /// + /// Calculates where to move the cursor to by projecting a point from + /// the preview image onto the desktop and using that as the target location. + /// + /// + /// The preview image origin is (0, 0) but the desktop origin may be non-zero, + /// or even negative if the primary monitor is not the at the top-left of the + /// entire desktop rectangle, so results may contain negative coordinates. + /// + public static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds) + { + return previewLocation + .Scale(previewSize.ScaleToFitRatio(desktopBounds.Size)) + .Offset(desktopBounds.Location); + } + + /// + /// Get the current position of the cursor. + /// + public static PointInfo GetCursorPosition() + { + var lpPoint = new LPPOINT(new POINT(0, 0)); + var result = User32.GetCursorPos(lpPoint); + if (!result) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + + var point = lpPoint.ToStructure(); + lpPoint.Free(); + + return new PointInfo( + point.x, point.y); + } + + /// + /// Moves the cursor to the specified location. + /// + /// + /// See https://github.com/mikeclayton/FancyMouse/pull/3 + /// + public static void SetCursorPosition(PointInfo location) + { + // set the new cursor position *twice* - the cursor sometimes end up in + // the wrong place if we try to cross the dead space between non-aligned + // monitors - e.g. when trying to move the cursor from (a) to (b) we can + // *sometimes* - for no clear reason - end up at (c) instead. + // + // +----------------+ + // |(c) (b) | + // | | + // | | + // | | + // +---------+ | + // | (a) | | + // +---------+----------------+ + // + // setting the position a second time seems to fix this and moves the + // cursor to the expected location (b) + var point = location.ToPoint(); + for (var i = 0; i < 2; i++) + { + var result = User32.SetCursorPos(point.X, point.Y); + if (!result) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + } + + // temporary workaround for issue #1273 + MouseHelper.SimulateMouseMovementEvent(location); + } + + /// + /// Sends an input simulating an absolute mouse move to the new location. + /// + /// + /// See https://github.com/microsoft/PowerToys/issues/24523 + /// https://github.com/microsoft/PowerToys/pull/24527 + /// + public static void SimulateMouseMovementEvent(PointInfo location) + { + var inputs = new User32.INPUT[] + { + new( + type: User32.INPUT_TYPE.INPUT_MOUSE, + data: new User32.INPUT.DUMMYUNIONNAME( + mi: new User32.MOUSEINPUT( + dx: (int)MouseHelper.CalculateAbsoluteCoordinateX(location.X), + dy: (int)MouseHelper.CalculateAbsoluteCoordinateY(location.Y), + mouseData: 0, + dwFlags: User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | User32.MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE, + time: 0, + dwExtraInfo: ULONG_PTR.Null))), + }; + var result = User32.SendInput( + (uint)inputs.Length, + new User32.LPINPUT(inputs), + User32.INPUT.Size * inputs.Length); + if (result != inputs.Length) + { + throw new Win32Exception( + Marshal.GetLastWin32Error()); + } + } + + private static decimal CalculateAbsoluteCoordinateX(decimal x) + { + // If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. + // see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + return (x * 65535) / User32.GetSystemMetrics(User32.SYSTEM_METRICS_INDEX.SM_CXSCREEN); + } + + internal static decimal CalculateAbsoluteCoordinateY(decimal y) + { + // If MOUSEEVENTF_ABSOLUTE value is specified, dx and dy contain normalized absolute coordinates between 0 and 65,535. + // see https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + return (y * 65535) / User32.GetSystemMetrics(User32.SYSTEM_METRICS_INDEX.SM_CYSCREEN); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs new file mode 100644 index 0000000000..4a61e6a6b3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/ScreenHelper.cs @@ -0,0 +1,94 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Screen; +using MouseJumpUI.NativeMethods; +using static MouseJumpUI.NativeMethods.Core; +using static MouseJumpUI.NativeMethods.User32; + +namespace MouseJumpUI.Helpers; + +internal static class ScreenHelper +{ + /// + /// Duplicates functionality available in System.Windows.Forms.SystemInformation + /// to reduce the dependency on WinForms + /// + public static RectangleInfo GetVirtualScreen() + { + return new( + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN), + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_YVIRTUALSCREEN), + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CXVIRTUALSCREEN), + User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN)); + } + + public static IEnumerable GetAllScreens() + { + // enumerate the monitors attached to the system + var hMonitors = new List(); + var result = User32.EnumDisplayMonitors( + HDC.Null, + LPCRECT.Null, + (unnamedParam1, unnamedParam2, unnamedParam3, unnamedParam4) => + { + hMonitors.Add(unnamedParam1); + return true; + }, + LPARAM.Null); + if (!result) + { + throw new Win32Exception( + $"{nameof(User32.EnumDisplayMonitors)} failed with return code {result.Value}"); + } + + // get detailed info about each monitor + foreach (var hMonitor in hMonitors) + { + var monitorInfoPtr = new LPMONITORINFO( + new MONITORINFO((uint)MONITORINFO.Size, RECT.Empty, RECT.Empty, 0)); + result = User32.GetMonitorInfoW(hMonitor, monitorInfoPtr); + if (!result) + { + throw new Win32Exception( + $"{nameof(User32.GetMonitorInfoW)} failed with return code {result.Value}"); + } + + var monitorInfo = monitorInfoPtr.ToStructure(); + monitorInfoPtr.Free(); + + yield return new ScreenInfo( + handle: hMonitor, + primary: monitorInfo.dwFlags.HasFlag(User32.MONITOR_INFO_FLAGS.MONITORINFOF_PRIMARY), + displayArea: new RectangleInfo( + monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.top, + monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top), + workingArea: new RectangleInfo( + monitorInfo.rcWork.left, + monitorInfo.rcWork.top, + monitorInfo.rcWork.right - monitorInfo.rcWork.left, + monitorInfo.rcWork.bottom - monitorInfo.rcWork.top)); + } + } + + public static HMONITOR MonitorFromPoint( + PointInfo pt) + { + var hMonitor = User32.MonitorFromPoint( + new((int)pt.X, (int)pt.Y), + User32.MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST); + if (hMonitor.IsNull) + { + throw new InvalidOperationException($"no monitor found for point {pt}"); + } + + return hMonitor; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs new file mode 100644 index 0000000000..8a8fa9110a --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs @@ -0,0 +1,95 @@ +// 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. + +using System.Windows.Forms; + +namespace MouseJumpUI; + +partial class MainForm +{ + + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + panel1 = new Panel(); + Thumbnail = new PictureBox(); + panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)Thumbnail).BeginInit(); + SuspendLayout(); + // + // panel1 + // + panel1.BackColor = System.Drawing.SystemColors.Highlight; + panel1.Controls.Add(Thumbnail); + panel1.Dock = DockStyle.Fill; + panel1.Location = new System.Drawing.Point(0, 0); + panel1.Name = "panel1"; + panel1.Padding = new Padding(5); + panel1.Size = new System.Drawing.Size(800, 450); + panel1.TabIndex = 1; + // + // Thumbnail + // + Thumbnail.BackColor = System.Drawing.SystemColors.Control; + Thumbnail.Dock = DockStyle.Fill; + Thumbnail.Location = new System.Drawing.Point(5, 5); + Thumbnail.Name = "Thumbnail"; + Thumbnail.Size = new System.Drawing.Size(790, 440); + Thumbnail.SizeMode = PictureBoxSizeMode.Normal; + Thumbnail.TabIndex = 1; + Thumbnail.TabStop = false; + Thumbnail.Click += Thumbnail_Click; + // + // MainForm + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(800, 450); + Controls.Add(panel1); + FormBorderStyle = FormBorderStyle.None; + Icon = (System.Drawing.Icon)resources.GetObject("$this.Icon"); + KeyPreview = true; + Name = "MainForm"; + ShowInTaskbar = false; + StartPosition = FormStartPosition.Manual; + Text = "MouseJump"; + TopMost = true; + Deactivate += MainForm_Deactivate; + Load += MainForm_Load; + KeyDown += MainForm_KeyDown; + panel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)Thumbnail).EndInit(); + ResumeLayout(false); + } + + #endregion + + private Panel panel1; + private PictureBox Thumbnail; + +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs new file mode 100644 index 0000000000..7dbc1c6dd4 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs @@ -0,0 +1,307 @@ +// 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. + +using System; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Windows.Forms; +using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; +using MouseJumpUI.Helpers; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Layout; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI; + +internal partial class MainForm : Form +{ + public MainForm(MouseJumpSettings settings) + { + this.InitializeComponent(); + this.Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + this.ShowThumbnail(); + } + + public MouseJumpSettings Settings + { + get; + } + + private void MainForm_Load(object sender, EventArgs e) + { + } + + private void MainForm_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape) + { + this.OnDeactivate(EventArgs.Empty); + return; + } + + // map screens to their screen number in "System > Display" + var screens = ScreenHelper.GetAllScreens() + .Select((screen, index) => new { Screen = screen, Index = index, Number = index + 1 }) + .ToList(); + + var currentLocation = MouseHelper.GetCursorPosition(); + var currentScreenHandle = ScreenHelper.MonitorFromPoint(currentLocation); + var currentScreen = screens + .Single(item => item.Screen.Handle == currentScreenHandle.Value); + var targetScreenNumber = default(int?); + + if (((e.KeyCode >= Keys.D1) && (e.KeyCode <= Keys.D9)) + || ((e.KeyCode >= Keys.NumPad1) && (e.KeyCode <= Keys.NumPad9))) + { + // number keys 1-9 or numpad keys 1-9 - move to the numbered screen + var screenNumber = e.KeyCode - Keys.D0; + if (screenNumber <= screens.Count) + { + targetScreenNumber = screenNumber; + } + } + else if (e.KeyCode == Keys.P) + { + // "P" - move to the primary screen + targetScreenNumber = screens.Single(item => item.Screen.Primary).Number; + } + else if (e.KeyCode == Keys.Left) + { + // move to the previous screen + targetScreenNumber = currentScreen.Number == 1 + ? screens.Count + : currentScreen.Number - 1; + } + else if (e.KeyCode == Keys.Right) + { + // move to the next screen + targetScreenNumber = currentScreen.Number == screens.Count + ? 1 + : currentScreen.Number + 1; + } + else if (e.KeyCode == Keys.Home) + { + // move to the first screen + targetScreenNumber = 1; + } + else if (e.KeyCode == Keys.End) + { + // move to the last screen + targetScreenNumber = screens.Count; + } + + if (targetScreenNumber.HasValue) + { + MouseHelper.SetCursorPosition( + screens[targetScreenNumber.Value - 1].Screen.Bounds.Midpoint); + this.OnDeactivate(EventArgs.Empty); + } + } + + private void MainForm_Deactivate(object sender, EventArgs e) + { + this.Close(); + + if (this.Thumbnail.Image is not null) + { + var tmp = this.Thumbnail.Image; + this.Thumbnail.Image = null; + tmp.Dispose(); + } + } + + private void Thumbnail_Click(object sender, EventArgs e) + { + var mouseEventArgs = (MouseEventArgs)e; + Logger.LogInfo(string.Join( + '\n', + $"Reporting mouse event args", + $"\tbutton = {mouseEventArgs.Button}", + $"\tlocation = {mouseEventArgs.Location}")); + + if (mouseEventArgs.Button == MouseButtons.Left) + { + // plain click - move mouse pointer + var virtualScreen = ScreenHelper.GetVirtualScreen(); + var scaledLocation = MouseHelper.GetJumpLocation( + new PointInfo(mouseEventArgs.X, mouseEventArgs.Y), + new SizeInfo(this.Thumbnail.Size), + virtualScreen); + Logger.LogInfo($"scaled location = {scaledLocation}"); + MouseHelper.SetCursorPosition(scaledLocation); + Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); + } + + this.OnDeactivate(EventArgs.Empty); + } + + public void ShowThumbnail() + { + var stopwatch = Stopwatch.StartNew(); + var layoutInfo = MainForm.GetLayoutInfo(this); + LayoutHelper.PositionForm(this, layoutInfo.FormBounds); + MainForm.RenderPreview(this, layoutInfo); + stopwatch.Stop(); + + // we have to activate the form to make sure the deactivate event fires + Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new Telemetry.MouseJumpTeleportCursorEvent()); + this.Activate(); + } + + private static LayoutInfo GetLayoutInfo(MainForm form) + { + // map screens to their screen number in "System > Display" + var screens = ScreenHelper.GetAllScreens() + .Select((screen, index) => new { Screen = screen, Index = index, Number = index + 1 }) + .ToList(); + foreach (var screen in screens) + { + Logger.LogInfo(string.Join( + '\n', + $"screen[{screen.Number}]", + $"\tprimary = {screen.Screen.Primary}", + $"\tdisplay area = {screen.Screen.DisplayArea}", + $"\tworking area = {screen.Screen.WorkingArea}")); + } + + // collect together some values that we need for calculating layout + var activatedLocation = MouseHelper.GetCursorPosition(); + var activatedScreenHandle = ScreenHelper.MonitorFromPoint(activatedLocation); + var activatedScreenIndex = screens + .Single(item => item.Screen.Handle == activatedScreenHandle.Value) + .Index; + + var layoutConfig = new LayoutConfig( + virtualScreenBounds: ScreenHelper.GetVirtualScreen(), + screens: screens.Select(item => item.Screen).ToList(), + activatedLocation: activatedLocation, + activatedScreenIndex: activatedScreenIndex, + activatedScreenNumber: activatedScreenIndex + 1, + maximumFormSize: new( + form.Settings.Properties.ThumbnailSize.Width, + form.Settings.Properties.ThumbnailSize.Height), + formPadding: new( + form.panel1.Padding.Left, + form.panel1.Padding.Top, + form.panel1.Padding.Right, + form.panel1.Padding.Bottom), + previewPadding: new(0)); + Logger.LogInfo(string.Join( + '\n', + $"Layout config", + $"-------------", + $"virtual screen = {layoutConfig.VirtualScreenBounds}", + $"activated location = {layoutConfig.ActivatedLocation}", + $"activated screen index = {layoutConfig.ActivatedScreenIndex}", + $"activated screen number = {layoutConfig.ActivatedScreenNumber}", + $"maximum form size = {layoutConfig.MaximumFormSize}", + $"form padding = {layoutConfig.FormPadding}", + $"preview padding = {layoutConfig.PreviewPadding}")); + + // calculate the layout coordinates for everything + var layoutInfo = LayoutHelper.CalculateLayoutInfo(layoutConfig); + Logger.LogInfo(string.Join( + '\n', + $"Layout info", + $"-----------", + $"form bounds = {layoutInfo.FormBounds}", + $"preview bounds = {layoutInfo.PreviewBounds}", + $"activated screen = {layoutInfo.ActivatedScreenBounds}")); + + return layoutInfo; + } + + private static void RenderPreview( + MainForm form, LayoutInfo layoutInfo) + { + var layoutConfig = layoutInfo.LayoutConfig; + + var stopwatch = Stopwatch.StartNew(); + + // initialize the preview image + var preview = new Bitmap( + (int)layoutInfo.PreviewBounds.Width, + (int)layoutInfo.PreviewBounds.Height, + PixelFormat.Format32bppArgb); + form.Thumbnail.Image = preview; + + using var previewGraphics = Graphics.FromImage(preview); + + DrawingHelper.DrawPreviewBackground(previewGraphics, layoutInfo.PreviewBounds, layoutInfo.ScreenBounds); + + var desktopHwnd = HWND.Null; + var desktopHdc = HDC.Null; + var previewHdc = HDC.Null; + try + { + // sort the source and target screen areas, putting the activated screen first + // (we need to capture and draw the activated screen before we show the form + // because otherwise we'll capture the form as part of the screenshot!) + var sourceScreens = layoutConfig.Screens + .Where((_, idx) => idx == layoutConfig.ActivatedScreenIndex) + .Union(layoutConfig.Screens.Where((_, idx) => idx != layoutConfig.ActivatedScreenIndex)) + .Select(screen => screen.Bounds) + .ToList(); + var targetScreens = layoutInfo.ScreenBounds + .Where((_, idx) => idx == layoutConfig.ActivatedScreenIndex) + .Union(layoutInfo.ScreenBounds.Where((_, idx) => idx != layoutConfig.ActivatedScreenIndex)) + .ToList(); + + DrawingHelper.EnsureDesktopDeviceContext(ref desktopHwnd, ref desktopHdc); + DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); + + var placeholdersDrawn = false; + for (var i = 0; i < sourceScreens.Count; i++) + { + DrawingHelper.DrawPreviewScreen( + desktopHdc, previewHdc, sourceScreens[i], targetScreens[i]); + + // show the placeholder images and show the form if it looks like it might take + // a while to capture the remaining screenshot images (but only if there are any) + if ((i < (sourceScreens.Count - 1)) && (stopwatch.ElapsedMilliseconds > 250)) + { + // we need to release the device context handle before we draw the placeholders + // using the Graphics object otherwise we'll get an error from GDI saying + // "Object is currently in use elsewhere" + DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); + + if (!placeholdersDrawn) + { + // draw placeholders for any undrawn screens + DrawingHelper.DrawPreviewScreenPlaceholders( + previewGraphics, + targetScreens.Where((_, idx) => idx > i)); + placeholdersDrawn = true; + } + + MainForm.RefreshPreview(form); + + // we've still got more screens to draw so open the device context again + DrawingHelper.EnsurePreviewDeviceContext(previewGraphics, ref previewHdc); + } + } + } + finally + { + DrawingHelper.FreeDesktopDeviceContext(ref desktopHwnd, ref desktopHdc); + DrawingHelper.FreePreviewDeviceContext(previewGraphics, ref previewHdc); + } + + MainForm.RefreshPreview(form); + stopwatch.Stop(); + } + + private static void RefreshPreview(MainForm form) + { + if (!form.Visible) + { + form.Show(); + } + + form.Thumbnail.Refresh(); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.resx b/src/modules/MouseUtils/MouseJumpUI/MainForm.resx new file mode 100644 index 0000000000..e33d2ec3e8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.resx @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA + AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP9kdJz/ZHSc/2R0nP9kdJz/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA/2R0nP9kdJz/ZHSc/2R0nP9kdJz/ZHSc/wAAAP8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/2R0nP9kdJz/ZHSc/2R0nP9kdJz/ZHSc/wAA + AP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP9kdJz/ZHSc/2R0 + nP9kdJz/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/8bi + 4/8AAAD/AAAA/wAAAP8AAAD/xuLj/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA/8bi4//G4uP/xuLj/5Okpf8kISD/k6Sl/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/5Ok + pf8kISD/k6Sl/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/yQhIP8kISD/JCEg/8bi4//G4uP/xuLj/8bi + 4//G4uP/xuLj/yQhIP8kISD/JCEg/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4//G4uP/xuLj/5Okpf8kISD/k6Sl/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/5Okpf8kISD/k6Sl/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi + 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAA + AP8AAAD/AAAA/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/wAA + AP8AAAD/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi + 4//G4uP/AAAA/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/AAAA/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi + 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CY + tf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CY + tf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY + tf+AmLX/gJi1/wAAAP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/AAAA/4CY + tf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4/8AAAD/gJi1/4CY + tf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/AAAA/wAAAP/G4uP/xuLj/8bi4//G4uP/AAAA/wAA + AP/G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAAAP8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi + 4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAD/AAAA/wAA + AP8AAAD/AAAAAAAAAP/G4uP/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAA + AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CY + tf8AAAD/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAA + AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CY + tf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/wAA + AP/G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AP/G4uP/AAAA/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAA + AP+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD/xuLj/wAAAP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/xuLj/8bi + 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi + 4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///////wAA////////AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA///8P///AAD///gf//8AAP//8A///wAA///gB/// + AAD//8AD//8AAP//gAH//wAA//8AAP//AAD//gAAf/8AAP/8AAA//wAA//wAAD//AAD/+AAAH/8AAP/4 + AAAf/wAA//AAAA//AAD/4AAAB/8AAP/AAAAD/wAA/8AAAAP/AAD/gAAAAf8AAP8AAAAA/wAA/wAAAAD/ + AAD/AAQgAP8AAP8AD/AA/wAA/wAf+AD/AAD/AD/8AP8AAP+AP/wB/wAA/8B//gP/AAD/wH/+A/8AAP/g + //8H/wAA//H//4//AAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA////////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2R0nP9kdJz/ZHSc/2R0nP8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9kdJz/ZHSc/2R0nP9kdJz/ZHSc/2R0 + nP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/2R0nP9kdJz/ZHSc/2R0 + nP9kdJz/ZHSc/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/2R0 + nP9kdJz/ZHSc/2R0nP8AAAD/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi + 4//G4uP/AAAA/wAAAP8AAAD/AAAA/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi + 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AP/G4uP/xuLj/8bi4/+TpKX/JCEg/5Okpf/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/+TpKX/JCEg/5Ok + pf/G4uP/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA/8bi4//G4uP/xuLj/yQhIP8kISD/JCEg/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/yQh + IP8kISD/JCEg/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi4//G4uP/k6Sl/yQhIP+TpKX/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/k6Sl/yQhIP+TpKX/xuLj/8bi4//G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP8AAAD/AAAA/8bi4//G4uP/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/wAAAP8AAAD/AAAA/8bi4//G4uP/AAAA/wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi + 4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi + 4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY + tf+AmLX/AAAA/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY + tf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/gJi1/4CY + tf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi4/8AAAD/gJi1/4CY + tf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CY + tf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/wAA + AP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAAAP/G4uP/xuLj/wAA + AP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4//G4uP/xuLj/8bi4//G4uP/xuLj/8bi + 4//G4uP/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAA/8bi + 4/8AAAD/gJi1/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/AAAA/wAAAP/G4uP/xuLj/8bi + 4//G4uP/AAAA/wAAAP/G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAA + AP8AAAD/xuLj/wAAAP+AmLX/gJi1/4CYtf+AmLX/gJi1/4CYtf8AAAD/xuLj/8bi4/8AAAD/AAAAAAAA + AP8AAAD/AAAA/wAAAP8AAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/gJi1/wAA + AP/G4uP/AAAA/wAAAP/G4uP/AAAA/4CYtf+AmLX/gJi1/4CYtf+AmLX/AAAA/8bi4//G4uP/AAAA/wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CYtf+AmLX/gJi1/4CY + tf+AmLX/AAAA/8bi4/8AAAD/AAAA/8bi4/8AAAD/gJi1/4CYtf+AmLX/gJi1/wAAAP/G4uP/xuLj/wAA + AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi4//G4uP/AAAA/4CY + tf+AmLX/gJi1/4CYtf8AAAD/xuLj/wAAAP8AAAD/xuLj/8bi4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi + 4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8bi + 4/8AAAD/gJi1/4CYtf+AmLX/AAAA/8bi4//G4uP/AAAA/wAAAAAAAAD/xuLj/8bi4/8AAAD/gJi1/wAA + AP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD/xuLj/8bi4/8AAAD/gJi1/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAD/xuLj/wAA + AP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD/xuLj/wAAAP+AmLX/AAAA/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAA + AP/G4uP/xuLj/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/wAAAP/G4uP/xuLj/wAAAP8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi4/8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/G4uP/xuLj/8bi4/8AAAD/AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////D////gf///wD///4Af//8AD//+A + Af//AAD//gAAf/wAAD/8AAA/+AAAH/gAAB/wAAAP4AAAB8AAAAPAAAADgAAAAQAAAAAAAAAAAAQgAAAP + 8AAAH/gAAD/8AIA//AHAf/4DwH/+A+D//wfx//+P//////////8oAAAAEAAAACAAAAABACAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAyOk7/MjpO/wAA + AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAyOk7/ZHSc/2R0 + nP8yOk7/AAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECVqqr/laqq/zI6 + Tv8yOk7/laqq/5Wqqv8AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECVqqr/xuLj/8bi + 4//G4uP/xuLj/8bi4//G4uP/laqq/wAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjcXL/xuLj/0BC + Qf+RoqP/xuLj/8bi4/+RoqP/QEJB/8bi4/9jcXL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAxuLj/8bi + 4/+RoqP/udPU/8bi4//G4uP/udPU/5Gio//G4uP/xuLj/wAAAIAAAAAAAAAAAAAAAAAAAABAlaqq/1Jf + Zv9ATFv/laqq/8bi4//G4uP/xuLj/8bi4/+Vqqr/QExb/1JfZv+Vqqr/AAAAQAAAAAAAAAAAY3Fy/1Jf + Zv+AmLX/gJi1/0BMW//G4uP/xuLj/8bi4//G4uP/QExb/4CYtf+AmLX/Ul9m/2Nxcv8AAAAAQktMv5Wq + qv9gcoj/gJi1/4CYtf9SX2b/xuLj/8bi4//G4uP/xuLj/1JfZv+AmLX/gJi1/2ByiP+Vqqr/QktMv2Nx + cv9ATFv/gJi1/4CYtf9gcoj/laqq/wAAAL9jcXL/Y3Fy/wAAAL+Vqqr/YHKI/4CYtf+AmLX/QExb/2Nx + cv9jcXL/QExb/4CYtf9gcoj/laqq/0JLTL8AAAAAAAAAAAAAAAAAAAAAQktMv5Wqqv9gcoj/gJi1/0BM + W/9jcXL/QktMv5Wqqv9gcoj/Ul9m/2Nxcv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABjcXL/Ul9m/2By + iP+Vqqr/QktMvwAAAABjcXL/Ul9m/5Wqqv8AAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJWq + qv9SX2b/Y3Fy/wAAAAAAAAAAAAAAQGNxcv9CS0y/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABCS0y/Y3Fy/wAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//rEH8P6xB+B+sQfAPrEHgB6xB4AesQcADrEGAAaxBgAGsQQAA + rEEAAKxBA8CsQQfgrEGH4axBj/GsQf//rEE= + + + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs new file mode 100644 index 0000000000..9437bf0531 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PaddingInfo.cs @@ -0,0 +1,60 @@ +// 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. + +using System.Windows.Forms; + +namespace MouseJumpUI.Models.Drawing; + +/// +/// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods. +/// +public sealed class PaddingInfo +{ + public PaddingInfo(decimal all) + : this(all, all, all, all) + { + } + + public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom) + { + this.Left = left; + this.Top = top; + this.Right = right; + this.Bottom = bottom; + } + + public decimal Left + { + get; + } + + public decimal Top + { + get; + } + + public decimal Right + { + get; + } + + public decimal Bottom + { + get; + } + + public decimal Horizontal => this.Left + this.Right; + + public decimal Vertical => this.Top + this.Bottom; + + public override string ToString() + { + return "{" + + $"{nameof(this.Left)}={this.Left}," + + $"{nameof(this.Top)}={this.Top}," + + $"{nameof(this.Right)}={this.Right}," + + $"{nameof(this.Bottom)}={this.Bottom}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs new file mode 100644 index 0000000000..40e452a378 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/PointInfo.cs @@ -0,0 +1,53 @@ +// 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. + +using System.Drawing; + +namespace MouseJumpUI.Models.Drawing; + +/// +/// Immutable version of a System.Drawing.Point object with some extra utility methods. +/// +public sealed class PointInfo +{ + public PointInfo(decimal x, decimal y) + { + this.X = x; + this.Y = y; + } + + public PointInfo(Point point) + : this(point.X, point.Y) + { + } + + public decimal X + { + get; + } + + public decimal Y + { + get; + } + + public SizeInfo ToSize() + { + return new((int)this.X, (int)this.Y); + } + + public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor); + + public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y); + + public Point ToPoint() => new((int)this.X, (int)this.Y); + + public override string ToString() + { + return "{" + + $"{nameof(this.X)}={this.X}," + + $"{nameof(this.Y)}={this.Y}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs new file mode 100644 index 0000000000..af46b85470 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/RectangleInfo.cs @@ -0,0 +1,131 @@ +// 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. + +using System; +using System.Drawing; + +namespace MouseJumpUI.Models.Drawing; + +/// +/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods. +/// +public sealed class RectangleInfo +{ + public RectangleInfo(decimal x, decimal y, decimal width, decimal height) + { + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; + } + + public RectangleInfo(Rectangle rectangle) + : this(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height) + { + } + + public RectangleInfo(Point location, SizeInfo size) + : this(location.X, location.Y, size.Width, size.Height) + { + } + + public RectangleInfo(SizeInfo size) + : this(0, 0, size.Width, size.Height) + { + } + + public decimal X + { + get; + } + + public decimal Y + { + get; + } + + public decimal Width + { + get; + } + + public decimal Height + { + get; + } + + public decimal Left => this.X; + + public decimal Top => this.Y; + + public decimal Right => this.X + this.Width; + + public decimal Bottom => this.Y + this.Height; + + public SizeInfo Size => new(this.Width, this.Height); + + public PointInfo Location => new(this.X, this.Y); + + public decimal Area => this.Width * this.Height; + + /// + /// Adapted from https://github.com/dotnet/runtime + /// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs + /// + public bool Contains(RectangleInfo rect) => + (this.X <= rect.X) && (rect.X + rect.Width <= this.X + this.Width) && + (this.Y <= rect.Y) && (rect.Y + rect.Height <= this.Y + this.Height); + + public RectangleInfo Enlarge(PaddingInfo padding) => new( + this.X + padding.Left, + this.Y + padding.Top, + this.Width + padding.Horizontal, + this.Height + padding.Vertical); + + public RectangleInfo Offset(SizeInfo amount) => this.Offset(amount.Width, amount.Height); + + public RectangleInfo Offset(decimal dx, decimal dy) => new(this.X + dx, this.Y + dy, this.Width, this.Height); + + public RectangleInfo Scale(decimal scalingFactor) => new( + this.X * scalingFactor, + this.Y * scalingFactor, + this.Width * scalingFactor, + this.Height * scalingFactor); + + public RectangleInfo Center(PointInfo point) => new( + x: point.X - (this.Width / 2), + y: point.Y - (this.Height / 2), + width: this.Width, + height: this.Height); + + public PointInfo Midpoint => new( + x: this.X + (this.Width / 2), + y: this.Y + (this.Height / 2)); + + public RectangleInfo Clamp(RectangleInfo outer) + { + if ((this.Width > outer.Width) || (this.Height > outer.Height)) + { + throw new ArgumentException($"Value cannot be larger than {nameof(outer)}."); + } + + return new( + x: Math.Clamp(this.X, outer.X, outer.Right - this.Width), + y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height), + width: this.Width, + height: this.Height); + } + + public Rectangle ToRectangle() => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height); + + public override string ToString() + { + return "{" + + $"{nameof(this.Left)}={this.Left}," + + $"{nameof(this.Top)}={this.Top}," + + $"{nameof(this.Width)}={this.Width}," + + $"{nameof(this.Height)}={this.Height}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs new file mode 100644 index 0000000000..a91febc23c --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Drawing/SizeInfo.cs @@ -0,0 +1,87 @@ +// 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. + +using System; +using System.Drawing; + +namespace MouseJumpUI.Models.Drawing; + +/// +/// Immutable version of a System.Drawing.Size object with some extra utility methods. +/// +public sealed class SizeInfo +{ + public SizeInfo(decimal width, decimal height) + { + this.Width = width; + this.Height = height; + } + + public SizeInfo(Size size) + : this(size.Width, size.Height) + { + } + + public decimal Width + { + get; + } + + public decimal Height + { + get; + } + + public SizeInfo Negate() => new(-this.Width, -this.Height); + + public SizeInfo Shrink(PaddingInfo padding) => new(this.Width - padding.Horizontal, this.Height - padding.Vertical); + + public SizeInfo Intersect(SizeInfo size) => new( + Math.Min(this.Width, size.Width), + Math.Min(this.Height, size.Height)); + + public RectangleInfo PlaceAt(decimal x, decimal y) => new(x, y, this.Width, this.Height); + + public SizeInfo ScaleToFit(SizeInfo bounds) + { + var widthRatio = bounds.Width / this.Width; + var heightRatio = bounds.Height / this.Height; + return widthRatio.CompareTo(heightRatio) switch + { + < 0 => new(bounds.Width, this.Height * widthRatio), + 0 => bounds, + > 0 => new(this.Width * heightRatio, bounds.Height), + }; + } + + /// + /// Get the scaling ratio to scale obj by so that it fits inside the specified bounds + /// without distorting the aspect ratio. + /// + public decimal ScaleToFitRatio(SizeInfo bounds) + { + if (bounds.Width == 0 || bounds.Height == 0) + { + throw new ArgumentException($"{nameof(bounds.Width)} or {nameof(bounds.Height)} cannot be zero", nameof(bounds)); + } + + var widthRatio = bounds.Width / this.Width; + var heightRatio = bounds.Height / this.Height; + var scalingRatio = Math.Min(widthRatio, heightRatio); + + return scalingRatio; + } + + public Size ToSize() => new((int)this.Width, (int)this.Height); + + public Point ToPoint() => new((int)this.Width, (int)this.Height); + + public override string ToString() + { + return "{" + + $"{nameof(this.Width)}={this.Width}," + + $"{nameof(this.Height)}={this.Height}" + + "}"; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs new file mode 100644 index 0000000000..540a980a7c --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs @@ -0,0 +1,122 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using MouseJumpUI.Models.Drawing; +using MouseJumpUI.Models.Screen; + +namespace MouseJumpUI.Models.Layout; + +/// +/// Represents a collection of values needed for calculating the MainForm layout. +/// +public sealed class LayoutConfig +{ + public LayoutConfig( + RectangleInfo virtualScreenBounds, + List screens, + PointInfo activatedLocation, + int activatedScreenIndex, + int activatedScreenNumber, + SizeInfo maximumFormSize, + PaddingInfo formPadding, + PaddingInfo previewPadding) + { + // make sure the virtual screen entirely contains all of the individual screen bounds + ArgumentNullException.ThrowIfNull(virtualScreenBounds); + ArgumentNullException.ThrowIfNull(screens); + if (screens.Any(screen => !virtualScreenBounds.Contains(screen.Bounds))) + { + throw new ArgumentException($"'{nameof(virtualScreenBounds)}' must contain all of the screens in '{nameof(screens)}'", nameof(virtualScreenBounds)); + } + + this.VirtualScreenBounds = virtualScreenBounds; + this.Screens = new(screens.ToList()); + this.ActivatedLocation = activatedLocation; + this.ActivatedScreenIndex = activatedScreenIndex; + this.ActivatedScreenNumber = activatedScreenNumber; + this.MaximumFormSize = maximumFormSize; + this.FormPadding = formPadding; + this.PreviewPadding = previewPadding; + } + + /// + /// Gets the coordinates of the entire virtual screen. + /// + /// + /// The Virtual Screen is the bounding rectangle of all the monitors. + /// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen + /// + public RectangleInfo VirtualScreenBounds + { + get; + } + + /// + /// Gets a collection containing the individual screens connected to the system. + /// + public ReadOnlyCollection Screens + { + get; + } + + /// + /// Gets the point where the cursor was located when the form was activated. + /// + /// + /// The preview form will be centered on this location unless there are any + /// constraints such as being too close to edge of a screen, in which case + /// the form will be displayed centered as close as possible to this location. + /// + public PointInfo ActivatedLocation + { + get; + } + + /// + /// Gets the index of the screen the cursor was on when the form was activated. + /// The value is an index into the ScreenBounds array and is 0-indexed as a result. + /// + public int ActivatedScreenIndex + { + get; + } + + /// + /// Gets the screen number the cursor was on when the form was activated. + /// The value matches the screen numbering scheme in the "Display Settings" dialog + /// and is 1-indexed as a result. + /// + public int ActivatedScreenNumber + { + get; + } + + /// + /// Gets the maximum size of the screen preview form. + /// + public SizeInfo MaximumFormSize + { + get; + } + + /// + /// Gets the padding border around the screen preview form. + /// + public PaddingInfo FormPadding + { + get; + } + + /// + /// Gets the padding border inside the screen preview image. + /// + public PaddingInfo PreviewPadding + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs new file mode 100644 index 0000000000..8a2cf74154 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs @@ -0,0 +1,112 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.Models.Layout; + +public sealed class LayoutInfo +{ + public sealed class Builder + { + public Builder() + { + this.ScreenBounds = new(); + } + + public LayoutConfig? LayoutConfig + { + get; + set; + } + + public RectangleInfo? FormBounds + { + get; + set; + } + + public RectangleInfo? PreviewBounds + { + get; + set; + } + + public List ScreenBounds + { + get; + set; + } + + public RectangleInfo? ActivatedScreenBounds + { + get; + set; + } + + public LayoutInfo Build() + { + return new LayoutInfo( + layoutConfig: this.LayoutConfig ?? throw new InvalidOperationException(), + formBounds: this.FormBounds ?? throw new InvalidOperationException(), + previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(), + screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(), + activatedScreenBounds: this.ActivatedScreenBounds ?? throw new InvalidOperationException()); + } + } + + public LayoutInfo( + LayoutConfig layoutConfig, + RectangleInfo formBounds, + RectangleInfo previewBounds, + IEnumerable screenBounds, + RectangleInfo activatedScreenBounds) + { + this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig)); + this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds)); + this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds)); + this.ScreenBounds = new( + (screenBounds ?? throw new ArgumentNullException(nameof(screenBounds))) + .ToList()); + this.ActivatedScreenBounds = activatedScreenBounds ?? throw new ArgumentNullException(nameof(activatedScreenBounds)); + } + + /// + /// Gets the original LayoutConfig settings used to calculate coordinates. + /// + public LayoutConfig LayoutConfig + { + get; + } + + /// + /// Gets the size and location of the preview form. + /// + public RectangleInfo FormBounds + { + get; + } + + /// + /// Gets the size and location of the preview image. + /// + public RectangleInfo PreviewBounds + { + get; + } + + public ReadOnlyCollection ScreenBounds + { + get; + } + + public RectangleInfo ActivatedScreenBounds + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs new file mode 100644 index 0000000000..d7791fb063 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Models/Screen/ScreenInfo.cs @@ -0,0 +1,46 @@ +// 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. + +using System; +using MouseJumpUI.Models.Drawing; + +namespace MouseJumpUI.Models.Screen; + +/// +/// Immutable version of a System.Windows.Forms.Screen object so we don't need to +/// take a dependency on WinForms just for screen info. +/// +public sealed class ScreenInfo +{ + internal ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea) + { + this.Handle = handle; + this.Primary = primary; + this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea)); + this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea)); + } + + public int Handle + { + get; + } + + public bool Primary + { + get; + } + + public RectangleInfo DisplayArea + { + get; + } + + public RectangleInfo Bounds => + this.DisplayArea; + + public RectangleInfo WorkingArea + { + get; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj new file mode 100644 index 0000000000..8d15866ace --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj @@ -0,0 +1,68 @@ + + + + PowerToys.MouseJumpUI + PowerToys MouseJumpUI + $(Version).0 + ..\..\..\..\$(Platform)\$(Configuration)\modules\MouseUtils\MouseJumpUI + false + false + true + true + MouseJumpUI.Program + true + true + enable + + + + + win10-x64 + + + win10-arm64 + + + + PerMonitorV2 + + + + {D962A009-834F-4EEC-AABB-430DF8F98E39} + WinExe + MouseJumpUI + PowerToys.MouseJumpUI + net7.0-windows10.0.19041.0 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + + + + + PowerToys.GPOWrapper + $(OutDir) + false + + + + true + DEBUG;TRACE + full + false + + + TRACE + pdbonly + prompt + true + + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs new file mode 100644 index 0000000000..851cb77a46 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/BOOL.cs @@ -0,0 +1,44 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A Boolean variable (should be TRUE or FALSE). + /// This type is declared in WinDef.h as follows: + /// typedef int BOOL; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct BOOL + { + public readonly int Value; + + public BOOL(int value) + { + this.Value = value; + } + + public BOOL(bool value) + { + this.Value = value ? 1 : 0; + } + + public static implicit operator bool(BOOL value) => value.Value != 0; + + public static implicit operator BOOL(bool value) => new(value); + + public static implicit operator int(BOOL value) => value.Value; + + public static implicit operator BOOL(int value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs new file mode 100644 index 0000000000..a44da78727 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/CRECT.cs @@ -0,0 +1,45 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// The CRECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect + /// + [SuppressMessage("Naming Rules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Name and value taken from Win32Api")] + internal readonly struct CRECT + { + public static readonly CRECT Empty = new(0, 0, 0, 0); + + public readonly LONG left; + public readonly LONG top; + public readonly LONG right; + public readonly LONG bottom; + + public CRECT( + LONG left, LONG top, LONG right, LONG bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public static int Size => + Marshal.SizeOf(typeof(CRECT)); + + public override string ToString() + { + return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs new file mode 100644 index 0000000000..9009b63c86 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/DWORD.cs @@ -0,0 +1,35 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 32-bit unsigned integer. The range is 0 through 4294967295 decimal. + /// This type is declared in IntSafe.h as follows: + /// typedef unsigned long DWORD; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct DWORD + { + public readonly uint Value; + + public DWORD(uint value) + { + this.Value = value; + } + + public static implicit operator uint(DWORD value) => value.Value; + + public static implicit operator DWORD(uint value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs new file mode 100644 index 0000000000..1df2624ed5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HANDLE.cs @@ -0,0 +1,41 @@ +// 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. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to an object. + /// This type is declared in WinNT.h as follows: + /// typedef PVOID HANDLE; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HANDLE + { + public static readonly HANDLE Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HANDLE(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HANDLE.Null.Value; + + public static implicit operator IntPtr(HANDLE value) => value.Value; + + public static implicit operator HANDLE(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs new file mode 100644 index 0000000000..b02a811c06 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HDC.cs @@ -0,0 +1,37 @@ +// 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. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to a device context (DC). + /// This type is declared in WinDef.h as follows: + /// typedef HANDLE HDC; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HDC + { + public static readonly HDC Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HDC(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HDC.Null.Value; + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs new file mode 100644 index 0000000000..047c654f6f --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HMONITOR.cs @@ -0,0 +1,49 @@ +// 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. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to a display monitor. + /// This type is declared in WinDef.h as follows: + /// if(WINVER >= 0x0500) typedef HANDLE HMONITOR; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HMONITOR + { + public static readonly HMONITOR Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HMONITOR(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HMONITOR.Null.Value; + + public static implicit operator int(HMONITOR value) => value.Value.ToInt32(); + + public static implicit operator HMONITOR(int value) => new(value); + + public static implicit operator IntPtr(HMONITOR value) => value.Value; + + public static implicit operator HMONITOR(IntPtr value) => new(value); + + public static implicit operator HANDLE(HMONITOR value) => new(value.Value); + + public static implicit operator HMONITOR(HANDLE value) => new(value.Value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs new file mode 100644 index 0000000000..e12f2846f3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/HWND.cs @@ -0,0 +1,37 @@ +// 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. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A handle to a window. + /// This type is declared in WinDef.h as follows: + /// typedef HANDLE HWND; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct HWND + { + public static readonly HWND Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public HWND(IntPtr value) + { + this.Value = value; + } + + public bool IsNull => this.Value == HWND.Null.Value; + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs new file mode 100644 index 0000000000..2d2dd82283 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LONG.cs @@ -0,0 +1,35 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 32-bit signed integer.The range is -2147483648 through 2147483647 decimal. + /// This type is declared in WinNT.h as follows: + /// typedef long LONG; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LONG + { + public readonly int Value; + + public LONG(int value) + { + this.Value = value; + } + + public static implicit operator int(LONG value) => value.Value; + + public static implicit operator LONG(int value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs new file mode 100644 index 0000000000..6a227f5e21 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPARAM.cs @@ -0,0 +1,38 @@ +// 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. +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A message parameter. + /// This type is declared in WinDef.h as follows: + /// typedef LONG_PTR LPARAM; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct LPARAM + { + public static readonly LPARAM Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPARAM(IntPtr value) + { + this.Value = value; + } + + public static implicit operator IntPtr(LPARAM value) => value.Value; + + public static implicit operator LPARAM(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs new file mode 100644 index 0000000000..d19abd9c39 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPCRECT.cs @@ -0,0 +1,49 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + internal readonly struct LPCRECT + { + public static readonly LPCRECT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPCRECT(IntPtr value) + { + this.Value = value; + } + + public LPCRECT(CRECT value) + { + this.Value = LPCRECT.ToPtr(value); + } + + private static IntPtr ToPtr(CRECT value) + { + var ptr = Marshal.AllocHGlobal(CRECT.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPCRECT value) => value.Value; + + public static implicit operator LPCRECT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs new file mode 100644 index 0000000000..db3423decd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPPOINT.cs @@ -0,0 +1,54 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + internal readonly struct LPPOINT + { + public static readonly LPPOINT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPPOINT(IntPtr value) + { + this.Value = value; + } + + public LPPOINT(POINT value) + { + this.Value = LPPOINT.ToPtr(value); + } + + private static IntPtr ToPtr(POINT value) + { + var ptr = Marshal.AllocHGlobal(POINT.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public POINT ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPPOINT value) => value.Value; + + public static implicit operator LPPOINT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs new file mode 100644 index 0000000000..087e22abe3 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/LPRECT.cs @@ -0,0 +1,48 @@ +// 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. +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + internal readonly struct LPRECT + { + public static readonly LPRECT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPRECT(IntPtr value) + { + this.Value = value; + } + + public LPRECT(RECT value) + { + this.Value = LPRECT.ToPtr(value); + } + + private static IntPtr ToPtr(RECT value) + { + var ptr = Marshal.AllocHGlobal(RECT.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public static implicit operator IntPtr(LPRECT value) => value.Value; + + public static implicit operator LPRECT(IntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs new file mode 100644 index 0000000000..d4513d6740 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/POINT.cs @@ -0,0 +1,47 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// The POINT structure defines the x- and y-coordinates of a point. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-point + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Names match Win32 api")] + internal readonly struct POINT + { + /// + /// Specifies the x-coordinate of the point. + /// + public readonly LONG x; + + /// + /// Specifies the y-coordinate of the point. + /// + public readonly LONG y; + + public POINT( + LONG x, + LONG y) + { + this.x = x; + this.y = y; + } + + public static int Size => + Marshal.SizeOf(typeof(POINT)); + + public override string ToString() + { + return $"x={this.x},y={this.y}"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs new file mode 100644 index 0000000000..ff107f6ad8 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/RECT.cs @@ -0,0 +1,45 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect + /// + [SuppressMessage("Naming Rules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Name and value taken from Win32Api")] + internal readonly struct RECT + { + public static readonly RECT Empty = new(0, 0, 0, 0); + + public readonly LONG left; + public readonly LONG top; + public readonly LONG right; + public readonly LONG bottom; + + public RECT( + LONG left, LONG top, LONG right, LONG bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public static int Size => + Marshal.SizeOf(typeof(RECT)); + + public override string ToString() + { + return $"left={this.left},top={this.top},right={this.right},bottom={this.bottom}"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs new file mode 100644 index 0000000000..e135212ec9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/UINT.cs @@ -0,0 +1,34 @@ +// 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. +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// An unsigned INT. The range is 0 through 4294967295 decimal. + /// This type is declared in WinDef.h as follows: + /// typedef unsigned int UINT; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct UINT + { + public readonly uint Value; + + public UINT(uint value) + { + this.Value = value; + } + + public static implicit operator uint(UINT value) => value.Value; + + public static implicit operator UINT(uint value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs new file mode 100644 index 0000000000..76d99d88be --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/ULONG_PTR.cs @@ -0,0 +1,44 @@ +// 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. + +using System; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// An unsigned LONG_PTR. + /// This type is declared in BaseTsd.h as follows: + /// C++ + /// #if defined(_WIN64) + /// typedef unsigned __int64 ULONG_PTR; + /// #else + /// typedef unsigned long ULONG_PTR; + /// #endif + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct ULONG_PTR + { + public static readonly ULONG_PTR Null = new(UIntPtr.Zero); + + public readonly UIntPtr Value; + + public ULONG_PTR(UIntPtr value) + { + this.Value = value; + } + + public static implicit operator UIntPtr(ULONG_PTR value) => value.Value; + + public static implicit operator ULONG_PTR(UIntPtr value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs new file mode 100644 index 0000000000..642d0b0514 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Core/WORD.cs @@ -0,0 +1,35 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Core +{ + /// + /// A 16-bit unsigned integer.The range is 0 through 65535 decimal. + /// This type is declared in WinDef.h as follows: + /// typedef unsigned short WORD; + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types + /// + internal readonly struct WORD + { + public readonly ushort Value; + + public WORD(ushort value) + { + this.Value = value; + } + + public static implicit operator ulong(WORD value) => value.Value; + + public static implicit operator WORD(ushort value) => new(value); + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs new file mode 100644 index 0000000000..994eeb7165 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs @@ -0,0 +1,37 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// A raster-operation code. These codes define how the color data for the source + /// rectangle is to be combined with the color data for the destination rectangle + /// to achieve the final color. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt + /// + internal enum ROP_CODE : uint + { + BLACKNESS = 0x00000042, + CAPTUREBLT = 0x40000000, + DSTINVERT = 0x00550009, + MERGECOPY = 0x00C000CA, + MERGEPAINT = 0x00BB0226, + NOMIRRORBITMAP = 0x80000000, + NOTSRCCOPY = 0x00330008, + NOTSRCERASE = 0x001100A6, + PATCOPY = 0x00F00021, + PATINVERT = 0x005A0049, + PATPAINT = 0x00FB0A09, + SRCAND = 0x008800C6, + SRCCOPY = 0x00CC0020, + SRCERASE = 0x00440328, + SRCINVERT = 0x00660046, + SRCPAINT = 0x00EE0086, + WHITENESS = 0x00FF0062, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs new file mode 100644 index 0000000000..575e092982 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs @@ -0,0 +1,26 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// The stretching mode. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode + /// + internal enum STRETCH_BLT_MODE : int + { + BLACKONWHITE = 1, + COLORONCOLOR = 3, + HALFTONE = 4, + WHITEONBLACK = 2, + STRETCH_ANDSCANS = STRETCH_BLT_MODE.BLACKONWHITE, + STRETCH_DELETESCANS = STRETCH_BLT_MODE.COLORONCOLOR, + STRETCH_HALFTONE = STRETCH_BLT_MODE.HALFTONE, + STRETCH_ORSCANS = STRETCH_BLT_MODE.WHITEONBLACK, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs new file mode 100644 index 0000000000..410af308d4 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs @@ -0,0 +1,27 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// The SetStretchBltMode function sets the bitmap stretching mode in the specified device context. + /// + /// + /// If the function succeeds, the return value is the previous stretching mode. + /// If the function fails, the return value is zero. + /// This function can return the following value. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setstretchbltmode + /// + [LibraryImport(Libraries.Gdi32)] + internal static partial int SetStretchBltMode( + HDC hdc, + STRETCH_BLT_MODE mode); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs new file mode 100644 index 0000000000..345391d666 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs @@ -0,0 +1,38 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class Gdi32 +{ + /// + /// The StretchBlt function copies a bitmap from a source rectangle into a destination + /// rectangle, stretching or compressing the bitmap to fit the dimensions of the + /// destination rectangle, if necessary. The system stretches or compresses the bitmap + /// according to the stretching mode currently set in the destination device context. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt + /// + [LibraryImport(Libraries.Gdi32)] + internal static partial BOOL StretchBlt( + HDC hdcDest, + int xDest, + int yDest, + int wDest, + int hDest, + HDC hdcSrc, + int xSrc, + int ySrc, + int wSrc, + int hSrc, + ROP_CODE rop); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs new file mode 100644 index 0000000000..59947db8fd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/Libraries.cs @@ -0,0 +1,11 @@ +// 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. + +namespace MouseJumpUI.NativeMethods; + +internal static class Libraries +{ + public const string Gdi32 = "gdi32"; + public const string User32 = "user32"; +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs new file mode 100644 index 0000000000..6d5a0687bb --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs @@ -0,0 +1,33 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The EnumDisplayMonitors function enumerates display monitors (including invisible + /// pseudo-monitors associated with the mirroring drivers) that intersect a region formed + /// by the intersection of a specified clipping rectangle and the visible region of a + /// device context. EnumDisplayMonitors calls an application-defined MonitorEnumProc + /// callback function once for each monitor that is enumerated. Note that + /// GetSystemMetrics (SM_CMONITORS) counts only the display monitors. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors + /// + [LibraryImport(Libraries.User32)] + internal static partial BOOL EnumDisplayMonitors( + HDC hdc, + LPCRECT lprcClip, + MONITORENUMPROC lpfnEnum, + LPARAM dwData); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs new file mode 100644 index 0000000000..ad55845cf9 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs @@ -0,0 +1,26 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The GetMonitorInfo function retrieves information about a display monitor. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors + /// + [LibraryImport(Libraries.User32)] + internal static partial BOOL GetMonitorInfoW( + HMONITOR hMonitor, + LPMONITORINFO lpmi); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs new file mode 100644 index 0000000000..ea0b79db9b --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs @@ -0,0 +1,31 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The GetWindowDC function retrieves the device context (DC) for the entire window, + /// including title bar, menus, and scroll bars. A window device context permits painting + /// anywhere in a window, because the origin of the device context is the upper-left + /// corner of the window instead of the client area. + /// + /// GetWindowDC assigns default attributes to the window device context each time it + /// retrieves the device context. Previous attributes are lost. + /// + /// + /// If the function succeeds, the return value is a handle to a device context for the specified window. + /// If the function fails, the return value is NULL, indicating an error or an invalid hWnd parameter. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowdc + /// + [LibraryImport(Libraries.User32)] + internal static partial HDC GetWindowDC( + HWND hWnd); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs new file mode 100644 index 0000000000..f44a2c7972 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs @@ -0,0 +1,53 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput + /// + internal readonly struct LPMONITORINFO + { + public static readonly LPMONITORINFO Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPMONITORINFO(IntPtr value) + { + this.Value = value; + } + + public LPMONITORINFO(MONITORINFO value) + { + this.Value = LPMONITORINFO.ToPtr(value); + } + + public MONITORINFO ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + private static IntPtr ToPtr(MONITORINFO value) + { + var ptr = Marshal.AllocHGlobal(MONITORINFO.Size); + Marshal.StructureToPtr(value, ptr, false); + return ptr; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs new file mode 100644 index 0000000000..0e366c8163 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs @@ -0,0 +1,22 @@ +// 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. + +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// A MonitorEnumProc function is an application-defined callback function that is called by the EnumDisplayMonitors function. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-monitorenumproc + /// + internal delegate BOOL MONITORENUMPROC( + HMONITOR unnamedParam1, + HDC unnamedParam2, + LPRECT unnamedParam3, + LPARAM unnamedParam4); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs new file mode 100644 index 0000000000..24fb270938 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs @@ -0,0 +1,39 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct MONITORINFO + { + public readonly DWORD cbSize; + public readonly RECT rcMonitor; + public readonly RECT rcWork; + public readonly MONITOR_INFO_FLAGS dwFlags; + + public MONITORINFO(DWORD cbSize, RECT rcMonitor, RECT rcWork, MONITOR_INFO_FLAGS dwFlags) + { + this.cbSize = cbSize; + this.rcMonitor = rcMonitor; + this.rcWork = rcWork; + this.dwFlags = dwFlags; + } + + public static int Size => + Marshal.SizeOf(typeof(INPUT)); + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs new file mode 100644 index 0000000000..5d5fc3b035 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs @@ -0,0 +1,24 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// Determines the function's return value if the point is not contained within any display monitor. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint + /// + internal enum MONITOR_FROM_FLAGS : uint + { + MONITOR_DEFAULTTONULL = 0x00000000, + MONITOR_DEFAULTTOPRIMARY = 0x00000001, + MONITOR_DEFAULTTONEAREST = 0x00000002, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs new file mode 100644 index 0000000000..f20e6a2f58 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs @@ -0,0 +1,22 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// A set of flags that represent attributes of the display monitor. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo + /// + internal enum MONITOR_INFO_FLAGS : uint + { + MONITORINFOF_PRIMARY = 1, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs new file mode 100644 index 0000000000..93878544e5 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs @@ -0,0 +1,26 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The MonitorFromPoint function retrieves a handle to the display monitor that contains a specified point. + /// + /// + /// If the point is contained by a display monitor, the return value is an HMONITOR handle to that display monitor. + /// If the point is not contained by a display monitor, the return value depends on the value of dwFlags. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfrompoint + /// + [LibraryImport(Libraries.User32)] + internal static partial HMONITOR MonitorFromPoint( + POINT pt, + MONITOR_FROM_FLAGS dwFlags); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs new file mode 100644 index 0000000000..a6f6caa355 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs @@ -0,0 +1,28 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// The ReleaseDC function releases a device context (DC), freeing it for use by other + /// applications. The effect of the ReleaseDC function depends on the type of DC. It + /// frees only common and window DCs. It has no effect on class or private DCs. + /// + /// + /// The return value indicates whether the DC was released. If the DC was released, the return value is 1. + /// If the DC was not released, the return value is zero. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-releasedc + /// + [LibraryImport(Libraries.User32)] + internal static partial int ReleaseDC( + HWND hWnd, + HDC hDC); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs new file mode 100644 index 0000000000..254b60eecd --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs @@ -0,0 +1,38 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Contains information about a simulated message generated by an input device + /// other than a keyboard or mouse. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-hardwareinput + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct HARDWAREINPUT + { + public readonly DWORD uMsg; + public readonly WORD wParamL; + public readonly WORD wParamH; + + public HARDWAREINPUT( + DWORD uMsg, + WORD wParamL, + WORD wParamH) + { + this.uMsg = uMsg; + this.wParamL = wParamL; + this.wParamH = wParamH; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs new file mode 100644 index 0000000000..1a4bcf8546 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs @@ -0,0 +1,51 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Used by SendInput to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct INPUT + { + public readonly INPUT_TYPE type; + public readonly DUMMYUNIONNAME data; + + public INPUT(INPUT_TYPE type, DUMMYUNIONNAME data) + { + this.type = type; + this.data = data; + } + + public static int Size => + Marshal.SizeOf(typeof(INPUT)); + + [StructLayout(LayoutKind.Explicit)] + public readonly struct DUMMYUNIONNAME + { + [FieldOffset(0)] + public readonly MOUSEINPUT mi; + [FieldOffset(0)] + public readonly KEYBDINPUT ki; + [FieldOffset(0)] + public readonly HARDWAREINPUT hi; + + public DUMMYUNIONNAME(MOUSEINPUT mi) + { + this.mi = mi; + } + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs new file mode 100644 index 0000000000..89e8f60218 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs @@ -0,0 +1,24 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// The type of the input event. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input + /// + internal enum INPUT_TYPE : uint + { + INPUT_MOUSE = 0, + INPUT_KEYBOARD = 1, + INPUT_HARDWARE = 2, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs new file mode 100644 index 0000000000..a5c9bc782b --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs @@ -0,0 +1,43 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Contains information about a simulated keyboard event. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct KEYBDINPUT + { + public readonly WORD wVk; + public readonly WORD wScan; + public readonly DWORD dwFlags; + public readonly DWORD time; + public readonly ULONG_PTR dwExtraInfo; + + public KEYBDINPUT( + WORD wVk, + WORD wScan, + DWORD dwFlags, + DWORD time, + ULONG_PTR dwExtraInfo) + { + this.wVk = wVk; + this.wScan = wScan; + this.dwFlags = dwFlags; + this.time = time; + this.dwExtraInfo = dwExtraInfo; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs new file mode 100644 index 0000000000..ff689a5931 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs @@ -0,0 +1,72 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput + /// + internal readonly struct LPINPUT + { + public static readonly LPINPUT Null = new(IntPtr.Zero); + + public readonly IntPtr Value; + + public LPINPUT(IntPtr value) + { + this.Value = value; + } + + public LPINPUT(INPUT[] values) + { + this.Value = LPINPUT.ToPtr(values); + } + + public INPUT ToStructure() + { + return Marshal.PtrToStructure(this.Value); + } + + public IEnumerable ToStructure(int count) + { + var ptr = this.Value; + var size = INPUT.Size; + for (var i = 0; i < count; i++) + { + yield return Marshal.PtrToStructure(this.Value); + ptr += size; + } + } + + private static IntPtr ToPtr(INPUT[] values) + { + var mem = Marshal.AllocHGlobal(INPUT.Size * values.Length); + var ptr = mem; + var size = INPUT.Size; + foreach (var value in values) + { + Marshal.StructureToPtr(value, ptr, true); + ptr += size; + } + + return mem; + } + + public void Free() + { + Marshal.FreeHGlobal(this.Value); + } + + public override string ToString() + { + return $"{this.GetType().Name}({this.Value})"; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs new file mode 100644 index 0000000000..80f9bbe2fc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs @@ -0,0 +1,46 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Contains information about a simulated mouse event. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + /// + [SuppressMessage("SA1307", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter", Justification = "Parameter name matches Win32 api")] + [StructLayout(LayoutKind.Sequential)] + internal readonly struct MOUSEINPUT + { + public readonly int dx; + public readonly int dy; + public readonly DWORD mouseData; + public readonly MOUSE_EVENT_FLAGS dwFlags; + public readonly DWORD time; + public readonly ULONG_PTR dwExtraInfo; + + public MOUSEINPUT( + int dx, + int dy, + DWORD mouseData, + MOUSE_EVENT_FLAGS dwFlags, + DWORD time, + ULONG_PTR dwExtraInfo) + { + this.dx = dx; + this.dy = dy; + this.mouseData = mouseData; + this.dwFlags = dwFlags; + this.time = time; + this.dwExtraInfo = dwExtraInfo; + } + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs new file mode 100644 index 0000000000..b6f3cba549 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs @@ -0,0 +1,34 @@ +// 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. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput + /// + [Flags] + internal enum MOUSE_EVENT_FLAGS : uint + { + MOUSEEVENTF_MOVE = 0x0001, + MOUSEEVENTF_LEFTDOWN = 0x0002, + MOUSEEVENTF_LEFTUP = 0x0004, + MOUSEEVENTF_RIGHTDOWN = 0x0008, + MOUSEEVENTF_RIGHTUP = 0x0010, + MOUSEEVENTF_MIDDLEDOWN = 0x0020, + MOUSEEVENTF_MIDDLEUP = 0x0040, + MOUSEEVENTF_XDOWN = 0x0080, + MOUSEEVENTF_XUP = 0x0100, + MOUSEEVENTF_WHEEL = 0x0800, + MOUSEEVENTF_HWHEEL = 0x1000, + MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000, + MOUSEEVENTF_VIRTUALDESK = 0x4000, + MOUSEEVENTF_ABSOLUTE = 0x8000, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs new file mode 100644 index 0000000000..b7bfba63bf --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs @@ -0,0 +1,28 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Synthesizes keystrokes, mouse motions, and button clicks. + /// + /// + /// The function returns the number of events that it successfully inserted into the keyboard or mouse input stream. + /// If the function returns zero, the input was already blocked by another thread. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial UINT SendInput( + UINT cInputs, + LPINPUT pInputs, + int cbSize); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs new file mode 100644 index 0000000000..f6cbfa75bc --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs @@ -0,0 +1,25 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves the position of the mouse cursor, in screen coordinates. + /// + /// + /// Returns nonzero if successful or zero otherwise. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial BOOL GetCursorPos( + LPPOINT lpPoint); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs new file mode 100644 index 0000000000..4d16b95e15 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs @@ -0,0 +1,24 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves a handle to the desktop window. The desktop window covers the entire + /// screen. The desktop window is the area on top of which other windows are painted. + /// + /// + /// The return value is a handle to the desktop window. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdesktopwindow + /// + [LibraryImport(Libraries.User32)] + internal static partial HWND GetDesktopWindow(); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs new file mode 100644 index 0000000000..7791c22963 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs @@ -0,0 +1,26 @@ +// 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. + +using System.Runtime.InteropServices; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Retrieves the specified system metric or system configuration setting. + /// + /// Note that all dimensions retrieved by GetSystemMetrics are in pixels. + /// + /// + /// If the function succeeds, the return value is the requested system metric or configuration setting. + /// If the function fails, the return value is 0. GetLastError does not provide extended error information. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics + /// + [LibraryImport(Libraries.User32)] + internal static partial int GetSystemMetrics( + SYSTEM_METRICS_INDEX smIndex); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs new file mode 100644 index 0000000000..b2ca6b3b68 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs @@ -0,0 +1,110 @@ +// 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. + +using System.Diagnostics.CodeAnalysis; + +namespace MouseJumpUI.NativeMethods; + +[SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] +internal static partial class User32 +{ + internal enum SYSTEM_METRICS_INDEX : uint + { + SM_ARRANGE = 56, + SM_CLEANBOOT = 67, + SM_CMONITORS = 80, + SM_CMOUSEBUTTONS = 43, + SM_CONVERTIBLESLATEMODE = 0x2003, + SM_CXBORDER = 5, + SM_CXCURSOR = 13, + SM_CXDLGFRAME = 7, + SM_CXDOUBLECLK = 36, + SM_CXDRAG = 68, + SM_CXEDGE = 45, + SM_CXFIXEDFRAME = SM_CXDLGFRAME, + SM_CXFOCUSBORDER = 83, + SM_CXFRAME = 32, + SM_CXFULLSCREEN = 16, + SM_CXHSCROLL = 21, + SM_CXHTHUMB = 10, + SM_CXICON = 11, + SM_CXICONSPACING = 38, + SM_CXMAXIMIZED = 61, + SM_CXMAXTRACK = 59, + SM_CXMENUCHECK = 71, + SM_CXMENUSIZE = 54, + SM_CXMIN = 28, + SM_CXMINIMIZED = 57, + SM_CXMINSPACING = 47, + SM_CXMINTRACK = 34, + SM_CXPADDEDBORDER = 92, + SM_CXSCREEN = 0, + SM_CXSIZE = 30, + SM_CXSIZEFRAME = SM_CXFRAME, + SM_CXSMICON = 49, + SM_CXSMSIZE = 52, + SM_CXVIRTUALSCREEN = 78, + SM_CXVSCROLL = 2, + SM_CYBORDER = 6, + SM_CYCAPTION = 4, + SM_CYCURSOR = 14, + SM_CYDLGFRAME = 8, + SM_CYDOUBLECLK = 37, + SM_CYDRAG = 69, + SM_CYEDGE = 46, + SM_CYFIXEDFRAME = SM_CYDLGFRAME, + SM_CYFOCUSBORDER = 84, + SM_CYFRAME = 33, + SM_CYFULLSCREEN = 17, + SM_CYHSCROLL = 3, + SM_CYICON = 12, + SM_CYICONSPACING = 39, + SM_CYKANJIWINDOW = 18, + SM_CYMAXIMIZED = 62, + SM_CYMAXTRACK = 60, + SM_CYMENU = 15, + SM_CYMENUCHECK = 72, + SM_CYMENUSIZE = 55, + SM_CYMIN = 29, + SM_CYMINIMIZED = 58, + SM_CYMINSPACING = 48, + SM_CYMINTRACK = 35, + SM_CYSCREEN = 1, + SM_CYSIZE = 31, + SM_CYSIZEFRAME = SM_CYFRAME, + SM_CYSMCAPTION = 51, + SM_CYSMICON = 50, + SM_CYSMSIZE = 53, + SM_CYVIRTUALSCREEN = 79, + SM_CYVSCROLL = 20, + SM_CYVTHUMB = 9, + SM_DBCSENABLED = 42, + SM_DEBUG = 22, + SM_DIGITIZER = 94, + SM_IMMENABLED = 82, + SM_MAXIMUMTOUCHES = 95, + SM_MEDIACENTER = 87, + SM_MENUDROPALIGNMENT = 40, + SM_MIDEASTENABLED = 74, + SM_MOUSEPRESENT = 19, + SM_MOUSEHORIZONTALWHEELPRESENT = 91, + SM_MOUSEWHEELPRESENT = 75, + SM_NETWORK = 63, + SM_PENWINDOWS = 41, + SM_REMOTECONTROL = 0x2001, + SM_REMOTESESSION = 0x1000, + SM_SAMEDISPLAYFORMA = 81, + SM_SECURE = 44, + SM_SERVERR2 = 89, + SM_SHOWSOUNDS = 70, + SM_SHUTTINGDOWN = 0x2000, + SM_SLOWMACHINE = 73, + SM_STARTER = 88, + SM_SWAPBUTTON = 23, + SM_SYSTEMDOCKED = 0x2004, + SM_TABLETPC = 86, + SM_XVIRTUALSCREEN = 76, + SM_YVIRTUALSCREEN = 77, + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs new file mode 100644 index 0000000000..b548148925 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs @@ -0,0 +1,28 @@ +// 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. + +using System.Runtime.InteropServices; +using static MouseJumpUI.NativeMethods.Core; + +namespace MouseJumpUI.NativeMethods; + +internal static partial class User32 +{ + /// + /// Moves the cursor to the specified screen coordinates. If the new coordinates are not within + /// the screen rectangle set by the most recent ClipCursor function call, the system automatically + /// adjusts the coordinates so that the cursor stays within the rectangle. + /// + /// + /// Returns nonzero if successful or zero otherwise. + /// To get extended error information, call GetLastError. + /// + /// + /// See https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcursorpos + /// + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial BOOL SetCursorPos( + int X, + int Y); +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Program.cs b/src/modules/MouseUtils/MouseJumpUI/Program.cs new file mode 100644 index 0000000000..7d3c385730 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Program.cs @@ -0,0 +1,71 @@ +// 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. + +using System; +using System.IO; +using System.Text.Json; +using System.Windows.Forms; +using ManagedCommon; +using Microsoft.PowerToys.Settings.UI.Library; + +namespace MouseJumpUI; + +internal static class Program +{ + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + Logger.InitializeLogger("\\MouseJump\\Logs"); + + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + + if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMouseJumpEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) + { + // TODO : Log message + Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); + return; + } + + if (Application.HighDpiMode != HighDpiMode.PerMonitorV2) + { + Logger.LogError("High dpi mode is not set to PerMonitorV2."); + return; + } + + var settings = Program.ReadSettings(); + var mainForm = new MainForm(settings); + + Application.Run(mainForm); + } + + private static MouseJumpSettings ReadSettings() + { + var settingsUtils = new SettingsUtils(); + var settingsPath = settingsUtils.GetSettingsFilePath(MouseJumpSettings.ModuleName); + if (!File.Exists(settingsPath)) + { + var scaffoldSettings = new MouseJumpSettings(); + settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), MouseJumpSettings.ModuleName); + } + + var settings = new MouseJumpSettings(); + try + { + settings = settingsUtils.GetSettings(MouseJumpSettings.ModuleName); + } + catch (Exception ex) + { + var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}"; + Logger.LogInfo(errorMessage); + Logger.LogDebug(errorMessage); + } + + return settings; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Properties/AssemblyInfo.cs b/src/modules/MouseUtils/MouseJumpUI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b44c65eead --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// 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. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("MouseJumpUI.UnitTests")] diff --git a/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpShowEvent.cs b/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpShowEvent.cs new file mode 100644 index 0000000000..7abf856d40 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpShowEvent.cs @@ -0,0 +1,16 @@ +// 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. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace MouseJumpUI.Telemetry +{ + [EventData] + public class MouseJumpShowEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpTeleportCursorEvent.cs b/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpTeleportCursorEvent.cs new file mode 100644 index 0000000000..40170b0d69 --- /dev/null +++ b/src/modules/MouseUtils/MouseJumpUI/Telemetry/MouseJumpTeleportCursorEvent.cs @@ -0,0 +1,16 @@ +// 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. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace MouseJumpUI.Telemetry +{ + [EventData] + public class MouseJumpTeleportCursorEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp b/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp index 0f84fe9632..6cedfe6d52 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp +++ b/src/modules/MouseUtils/MousePointerCrosshairs/InclusiveCrosshairs.cpp @@ -212,25 +212,25 @@ void InclusiveCrosshairs::UpdateCrosshairsPosition() float halfPixelAdjustment = m_crosshairs_thickness % 2 == 1 ? 0.5f : 0.0f; // Position crosshairs components around the mouse pointer. - m_left_crosshairs_border.Offset({ (float)ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_left_crosshairs_border.Size({ (float)ptCursor.x - (float)ptMonitorUpperLeft.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, (float)m_crosshairs_thickness + m_crosshairs_border_size * 2 }); - m_left_crosshairs.Offset({ (float)ptCursor.x - m_crosshairs_radius + halfPixelAdjustment * 2, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_left_crosshairs.Size({ (float)ptCursor.x - (float)ptMonitorUpperLeft.x - m_crosshairs_radius + halfPixelAdjustment * 2, (float)m_crosshairs_thickness }); + m_left_crosshairs_border.Offset({ ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, ptCursor.y + halfPixelAdjustment, .0f }); + m_left_crosshairs_border.Size({ ptCursor.x - ptMonitorUpperLeft.x - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, m_crosshairs_thickness + m_crosshairs_border_size * 2.f }); + m_left_crosshairs.Offset({ ptCursor.x - m_crosshairs_radius + halfPixelAdjustment * 2.f, ptCursor.y + halfPixelAdjustment, .0f }); + m_left_crosshairs.Size({ ptCursor.x - ptMonitorUpperLeft.x - m_crosshairs_radius + halfPixelAdjustment * 2, static_cast(m_crosshairs_thickness) }); - m_right_crosshairs_border.Offset({ (float)ptCursor.x + m_crosshairs_radius - m_crosshairs_border_size, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_right_crosshairs_border.Size({ (float)ptMonitorBottomRight.x - (float)ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size, (float)m_crosshairs_thickness + m_crosshairs_border_size * 2 }); - m_right_crosshairs.Offset({ (float)ptCursor.x + m_crosshairs_radius, (float)ptCursor.y + halfPixelAdjustment, .0f }); - m_right_crosshairs.Size({ (float)ptMonitorBottomRight.x - (float)ptCursor.x - m_crosshairs_radius, (float)m_crosshairs_thickness }); + m_right_crosshairs_border.Offset({static_cast(ptCursor.x) + m_crosshairs_radius - m_crosshairs_border_size, ptCursor.y + halfPixelAdjustment, .0f }); + m_right_crosshairs_border.Size({ static_cast(ptMonitorBottomRight.x) - ptCursor.x - m_crosshairs_radius + m_crosshairs_border_size, m_crosshairs_thickness + m_crosshairs_border_size * 2.f }); + m_right_crosshairs.Offset({ static_cast(ptCursor.x) + m_crosshairs_radius, ptCursor.y + halfPixelAdjustment, .0f }); + m_right_crosshairs.Size({ static_cast(ptMonitorBottomRight.x) - ptCursor.x - m_crosshairs_radius, static_cast(m_crosshairs_thickness) }); - m_top_crosshairs_border.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, .0f }); - m_top_crosshairs_border.Size({ (float)m_crosshairs_thickness + m_crosshairs_border_size * 2, (float)ptCursor.y - (float)ptMonitorUpperLeft.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2 }); - m_top_crosshairs.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y - m_crosshairs_radius + halfPixelAdjustment * 2, .0f }); - m_top_crosshairs.Size({ (float)m_crosshairs_thickness, (float)ptCursor.y - (float)ptMonitorUpperLeft.y - m_crosshairs_radius + halfPixelAdjustment * 2 }); + m_top_crosshairs_border.Offset({ ptCursor.x + halfPixelAdjustment, ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2, .0f }); + m_top_crosshairs_border.Size({ m_crosshairs_thickness + m_crosshairs_border_size * 2.f, ptCursor.y - ptMonitorUpperLeft.y - m_crosshairs_radius + m_crosshairs_border_size + halfPixelAdjustment * 2 }); + m_top_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, ptCursor.y - m_crosshairs_radius + halfPixelAdjustment * 2, .0f }); + m_top_crosshairs.Size({ static_cast(m_crosshairs_thickness), ptCursor.y - ptMonitorUpperLeft.y - m_crosshairs_radius + halfPixelAdjustment * 2 }); - m_bottom_crosshairs_border.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y + m_crosshairs_radius - m_crosshairs_border_size, .0f }); - m_bottom_crosshairs_border.Size({ (float)m_crosshairs_thickness + m_crosshairs_border_size * 2, (float)ptMonitorBottomRight.y - (float)ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size }); - m_bottom_crosshairs.Offset({ (float)ptCursor.x + halfPixelAdjustment, (float)ptCursor.y + m_crosshairs_radius, .0f }); - m_bottom_crosshairs.Size({ (float)m_crosshairs_thickness, (float)ptMonitorBottomRight.y - (float)ptCursor.y - m_crosshairs_radius }); + m_bottom_crosshairs_border.Offset({ ptCursor.x + halfPixelAdjustment, static_cast(ptCursor.y) + m_crosshairs_radius - m_crosshairs_border_size, .0f }); + m_bottom_crosshairs_border.Size({ m_crosshairs_thickness + m_crosshairs_border_size * 2.f, static_cast(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius + m_crosshairs_border_size }); + m_bottom_crosshairs.Offset({ ptCursor.x + halfPixelAdjustment, static_cast(ptCursor.y) + m_crosshairs_radius, .0f }); + m_bottom_crosshairs.Size({ static_cast(m_crosshairs_thickness), static_cast(ptMonitorBottomRight.y) - ptCursor.y - m_crosshairs_radius }); } @@ -238,7 +238,7 @@ LRESULT CALLBACK InclusiveCrosshairs::MouseHookProc(int nCode, WPARAM wParam, LP { if (nCode >= 0) { - MSLLHOOKSTRUCT* hookData = (MSLLHOOKSTRUCT*)lParam; + MSLLHOOKSTRUCT* hookData = reinterpret_cast(lParam); if (wParam == WM_MOUSEMOVE) { instance->UpdateCrosshairsPosition(); } @@ -362,7 +362,7 @@ bool InclusiveCrosshairs::MyRegisterClass(HINSTANCE hInstance) wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wc.hbrBackground = static_cast(GetStockObject(NULL_BRUSH)); wc.lpszClassName = m_className; if (!RegisterClassW(&wc)) diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp b/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp index 81c79c57b6..257d050e5e 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp +++ b/src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp @@ -222,7 +222,15 @@ public: { // Parse Opacity auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_OPACITY); - inclusiveCrosshairsSettings.crosshairsOpacity = (uint8_t)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsOpacity = value; + } + else + { + throw; + } } catch (...) { @@ -251,7 +259,16 @@ public: { // Parse Radius auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_RADIUS); - inclusiveCrosshairsSettings.crosshairsRadius = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsRadius = value; + } + else + { + throw; + } + } catch (...) { @@ -261,7 +278,16 @@ public: { // Parse Thickness auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_THICKNESS); - inclusiveCrosshairsSettings.crosshairsThickness = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsThickness = value; + } + else + { + throw; + } + } catch (...) { @@ -290,7 +316,15 @@ public: { // Parse border size auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_CROSSHAIRS_BORDER_SIZE); - inclusiveCrosshairsSettings.crosshairsBorderSize = (UINT)jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE); + int value = static_cast (jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + inclusiveCrosshairsSettings.crosshairsBorderSize = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/PowerOCR/PowerOCR/App.xaml.cs b/src/modules/PowerOCR/PowerOCR/App.xaml.cs index 7a83b93897..60c6c03b81 100644 --- a/src/modules/PowerOCR/PowerOCR/App.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/App.xaml.cs @@ -26,6 +26,8 @@ public partial class App : Application, IDisposable public App() { + Logger.InitializeLogger("\\TextExtractor\\Logs"); + NativeThreadCTS = new CancellationTokenSource(); } diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs b/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs index d5fa1a49ab..85fdae3083 100644 --- a/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs +++ b/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs @@ -24,7 +24,7 @@ using BitmapDecoder = Windows.Graphics.Imaging.BitmapDecoder; namespace PowerOCR; -internal class ImageMethods +internal sealed class ImageMethods { internal static ImageSource GetWindowBoundsImage(Window passedWindow) { diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/Logger.cs b/src/modules/PowerOCR/PowerOCR/Helpers/Logger.cs deleted file mode 100644 index 447ff6f97b..0000000000 --- a/src/modules/PowerOCR/PowerOCR/Helpers/Logger.cs +++ /dev/null @@ -1,79 +0,0 @@ -// 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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace PowerOCR.Helpers -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "TextExtractor\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType?.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs b/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs index d4301defc5..939f987ace 100644 --- a/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs +++ b/src/modules/PowerOCR/PowerOCR/Helpers/WindowUtilities.cs @@ -4,6 +4,7 @@ using System.Windows; using System.Windows.Forms; +using ManagedCommon; using Microsoft.PowerToys.Telemetry; using PowerOCR.Helpers; diff --git a/src/modules/PowerOCR/PowerOCR/Keyboard/EventMonitor.cs b/src/modules/PowerOCR/PowerOCR/Keyboard/EventMonitor.cs index 14d213c151..e0cf0a7ac5 100644 --- a/src/modules/PowerOCR/PowerOCR/Keyboard/EventMonitor.cs +++ b/src/modules/PowerOCR/PowerOCR/Keyboard/EventMonitor.cs @@ -12,7 +12,7 @@ namespace PowerOCR.Keyboard /// This class handles the interaction model when running from PowerToys Run. /// Handles activation through the event sent by the runner. /// - internal class EventMonitor + internal sealed class EventMonitor { public EventMonitor(System.Windows.Threading.Dispatcher dispatcher, System.Threading.CancellationToken exitToken) { diff --git a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs index 3ffe94c2e0..34f0f77160 100644 --- a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs +++ b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHook.cs @@ -10,7 +10,7 @@ using static PowerOCR.OSInterop; namespace PowerOCR.Keyboard; -internal class GlobalKeyboardHook : IDisposable +internal sealed class GlobalKeyboardHook : IDisposable { private IntPtr _windowsHookHandle; private IntPtr _user32LibraryHandle; @@ -39,7 +39,7 @@ internal class GlobalKeyboardHook : IDisposable internal event EventHandler? KeyboardPressed; - protected virtual void Dispose(bool disposing) + public void Dispose(bool disposing) { if (disposing) { diff --git a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs index b20bcd645a..23360de470 100644 --- a/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs +++ b/src/modules/PowerOCR/PowerOCR/Keyboard/GlobalKeyboardHookEventArgs.cs @@ -7,7 +7,7 @@ using static PowerOCR.OSInterop; namespace PowerOCR.Keyboard; -internal class GlobalKeyboardHookEventArgs : HandledEventArgs +internal sealed class GlobalKeyboardHookEventArgs : HandledEventArgs { internal GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; } diff --git a/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs b/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs index 5b66d0b937..66e1374569 100644 --- a/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/OCROverlay.xaml.cs @@ -9,6 +9,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; +using ManagedCommon; using Microsoft.PowerToys.Telemetry; using PowerOCR.Helpers; using PowerOCR.Settings; @@ -35,7 +36,7 @@ public partial class OCROverlay : Window private Point GetMousePos() => PointToScreen(Mouse.GetPosition(this)); - private Language? selectedLanguage = null; + private Language? selectedLanguage; private MenuItem cancelMenuItem; private System.Windows.Forms.Screen? CurrentScreen @@ -70,8 +71,8 @@ public partial class OCROverlay : Window foreach (Language language in possibleOcrLanguages) { MenuItem menuItem = new MenuItem() { Header = language.NativeName, Tag = language, IsCheckable = true }; - menuItem.IsChecked = language.DisplayName.Equals(selectedLanguageName); - if (language.DisplayName.Equals(selectedLanguageName)) + menuItem.IsChecked = language.DisplayName.Equals(selectedLanguageName, StringComparison.Ordinal); + if (language.DisplayName.Equals(selectedLanguageName, StringComparison.Ordinal)) { selectedLanguage = language; } diff --git a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj index bfb1a03791..5182b93440 100644 --- a/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj +++ b/src/modules/PowerOCR/PowerOCR/PowerOCR.csproj @@ -48,13 +48,14 @@ - - - + + + + diff --git a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs index 4019c0f354..d36bf19faa 100644 --- a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs +++ b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs @@ -7,6 +7,7 @@ using System.ComponentModel.Composition; using System.IO; using System.IO.Abstractions; using System.Threading; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/modules/PowerOCR/PowerOCR/app.manifest b/src/modules/PowerOCR/PowerOCR/app.manifest index 5102e74135..8bd61651e3 100644 --- a/src/modules/PowerOCR/PowerOCR/app.manifest +++ b/src/modules/PowerOCR/PowerOCR/app.manifest @@ -45,7 +45,7 @@ - + diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp index 08c5c79fe8..0f01aaf403 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/d2d_svg.cpp @@ -24,9 +24,9 @@ D2DSVG& D2DSVG::load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc) svg->GetRoot(root.put()); float tmp; winrt::check_hresult(root->GetAttributeValue(L"width", &tmp)); - svg_width = (int)tmp; + svg_width = static_cast(tmp); winrt::check_hresult(root->GetAttributeValue(L"height", &tmp)); - svg_height = (int)tmp; + svg_height = static_cast(tmp); return *this; } @@ -43,7 +43,7 @@ D2DSVG& D2DSVG::resize(int x, int y, int width, int height, float fill, float ma used_scale = std::min(used_scale, max_scale); } transform = transform * D2D1::Matrix3x2F::Scale(used_scale, used_scale, D2D1::Point2F(width / 2.0f, height / 2.0f)); - transform = transform * D2D1::Matrix3x2F::Translation((float)x, (float)y); + transform = transform * D2D1::Matrix3x2F::Translation(static_cast(x), static_cast(y)); return *this; } diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp index 87afa42930..7f25c4e32c 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/d2d_text.cpp @@ -47,7 +47,7 @@ void D2DText::write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F winrt::com_ptr brush; d2d_dc->CreateSolidColorBrush(color, brush.put()); d2d_dc->DrawText(text.c_str(), - (UINT32)text.length(), + static_cast(text.length()), format.get(), rect, brush.get()); diff --git a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp b/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp index 095e7dcba8..f4bf7e9cf6 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/d2d_window.cpp @@ -199,7 +199,7 @@ LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM w } case WM_MOVE: case WM_SIZE: - self->base_resize((unsigned)lparam & 0xFFFF, (unsigned)lparam >> 16); + self->base_resize(static_cast(lparam) & 0xFFFF, static_cast(lparam) >> 16); [[fallthrough]]; case WM_PAINT: self->base_render(); diff --git a/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp b/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp index ddf5e30680..7ef1e90301 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/overlay_window.cpp @@ -111,10 +111,10 @@ D2DOverlaySVG& D2DOverlaySVG::resize(int x, int y, int width, int height, float { auto scaled_top_left = transform.TransformPoint(thumbnail_top_left); auto scanled_bottom_right = transform.TransformPoint(thumbnail_bottom_right); - thumbnail_scaled_rect.left = (int)scaled_top_left.x; - thumbnail_scaled_rect.top = (int)scaled_top_left.y; - thumbnail_scaled_rect.right = (int)scanled_bottom_right.x; - thumbnail_scaled_rect.bottom = (int)scanled_bottom_right.y; + thumbnail_scaled_rect.left = static_cast(scaled_top_left.x); + thumbnail_scaled_rect.top = static_cast(scaled_top_left.y); + thumbnail_scaled_rect.right = static_cast(scanled_bottom_right.x); + thumbnail_scaled_rect.bottom = static_cast(scanled_bottom_right.y); } return *this; } @@ -156,10 +156,10 @@ ScaleResult D2DOverlaySVG::get_thumbnail_rect_and_scale(int x_offset, int y_offs float scale_v = fill * thumbnail_scaled_rect_heigh / window_cy; float use_scale = std::min(scale_h, scale_v); RECT thumb_rect; - thumb_rect.left = thumbnail_scaled_rect.left + (int)(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; - thumb_rect.right = thumbnail_scaled_rect.right - (int)(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; - thumb_rect.top = thumbnail_scaled_rect.top + (int)(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; - thumb_rect.bottom = thumbnail_scaled_rect.bottom - (int)(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; + thumb_rect.left = thumbnail_scaled_rect.left + static_cast(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; + thumb_rect.right = thumbnail_scaled_rect.right - static_cast(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset; + thumb_rect.top = thumbnail_scaled_rect.top + static_cast(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; + thumb_rect.bottom = thumbnail_scaled_rect.bottom - static_cast(thumbnail_scaled_rect_heigh - use_scale * window_cy) / 2 + y_offset; ScaleResult result; result.scale = use_scale; result.rect = thumb_rect; @@ -185,8 +185,8 @@ D2DOverlaySVG& D2DOverlaySVG::toggle_window_group(bool active) D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.210f; @@ -198,7 +198,7 @@ D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const { result.top = thumbnail_scaled_rect.top + height * 0.323f; result.bottom = thumbnail_scaled_rect.top + height * 0.398f; - result.left = (float)thumbnail_scaled_rect.right; + result.left = static_cast(thumbnail_scaled_rect.right); result.right = thumbnail_scaled_rect.right + width * 1.45f; } return result; @@ -206,8 +206,8 @@ D2D1_RECT_F D2DOverlaySVG::get_maximize_label() const D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.8f; @@ -219,7 +219,7 @@ D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const { result.top = thumbnail_scaled_rect.top + height * 0.725f; result.bottom = thumbnail_scaled_rect.top + height * 0.800f; - result.left = (float)thumbnail_scaled_rect.right; + result.left =static_cast(thumbnail_scaled_rect.right); result.right = thumbnail_scaled_rect.right + width * 1.45f; } return result; @@ -227,8 +227,8 @@ D2D1_RECT_F D2DOverlaySVG::get_minimize_label() const D2D1_RECT_F D2DOverlaySVG::get_snap_left() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.5f; @@ -240,7 +240,7 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_left() const { result.top = thumbnail_scaled_rect.top + height * 0.523f; result.bottom = thumbnail_scaled_rect.top + height * 0.598f; - result.left = (float)thumbnail_scaled_rect.right; + result.left = static_cast(thumbnail_scaled_rect.right); result.right = thumbnail_scaled_rect.right + width * 0.450f; } return result; @@ -248,8 +248,8 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_left() const D2D1_RECT_F D2DOverlaySVG::get_snap_right() const { D2D1_RECT_F result; - auto height = (float)(thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top); - auto width = (float)(thumbnail_scaled_rect.right - thumbnail_scaled_rect.left); + auto height = thumbnail_scaled_rect.bottom - thumbnail_scaled_rect.top; + auto width = thumbnail_scaled_rect.right - thumbnail_scaled_rect.left; if (width >= height) { result.top = thumbnail_scaled_rect.bottom + height * 0.5f; @@ -261,7 +261,7 @@ D2D1_RECT_F D2DOverlaySVG::get_snap_right() const { result.top = thumbnail_scaled_rect.top + height * 0.523f; result.bottom = thumbnail_scaled_rect.top + height * 0.598f; - result.left = (float)thumbnail_scaled_rect.right + width; + result.left = static_cast(thumbnail_scaled_rect.right + width); result.right = thumbnail_scaled_rect.right + width * 1.45f; } return result; @@ -390,7 +390,7 @@ void D2DOverlayWindow::show(HWND window, bool snappable) // Check if taskbar is auto-hidden. If so, don't display the number arrows APPBARDATA param = {}; param.cbSize = sizeof(APPBARDATA); - if ((UINT)SHAppBarMessage(ABM_GETSTATE, ¶m) != ABS_AUTOHIDE) + if (static_cast(SHAppBarMessage(ABM_GETSTATE, ¶m)) != ABS_AUTOHIDE) { tasklist_cv_mutex.lock(); tasklist_update = true; @@ -588,12 +588,12 @@ void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_ dy = -1; arrow.toggle_element(L"bottom", true); } - double arrow_ratio = (double)arrow.height() / arrow.width(); + double arrow_ratio = static_cast(arrow.height()) / arrow.width(); if (dy != 0) { // assume button is 25% wider than taller, +10% to make room for each of the arrows that are hidden - auto render_arrow_width = (int)(button.height * 1.25f * 1.2f); - auto render_arrow_height = (int)(render_arrow_width * arrow_ratio); + auto render_arrow_width = static_cast(button.height * 1.25f * 1.2f); + auto render_arrow_height = static_cast(render_arrow_width * arrow_ratio); arrow.resize((button.x + (button.width - render_arrow_width) / 2) + x_offset, (dy == -1 ? button.y - render_arrow_height : 0) + y_offset, render_arrow_width, @@ -605,8 +605,8 @@ void render_arrow(D2DSVG& arrow, TasklistButton& button, RECT window, float max_ else { // same as above - make room for the hidden arrow - auto render_arrow_height = (int)(button.height * 1.2f); - auto render_arrow_width = (int)(render_arrow_height / arrow_ratio); + auto render_arrow_height = static_cast(button.height * 1.2f); + auto render_arrow_width = static_cast(render_arrow_height / arrow_ratio); arrow.resize((dx == -1 ? button.x - render_arrow_width : 0) + x_offset, (button.y + (button.height - render_arrow_height) / 2) + y_offset, render_arrow_width, @@ -627,7 +627,7 @@ bool D2DOverlayWindow::show_thumbnail(const RECT& rect, double alpha) thumb_properties.dwFlags = DWM_TNP_SOURCECLIENTAREAONLY | DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY; thumb_properties.fSourceClientAreaOnly = FALSE; thumb_properties.fVisible = TRUE; - thumb_properties.opacity = (BYTE)(255 * alpha); + thumb_properties.opacity = static_cast(255 * alpha); thumb_properties.rcDestination = rect; if (DwmUpdateThumbnailProperties(thumbnail, &thumb_properties) != S_OK) { @@ -655,10 +655,10 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) d2d_device_context->Clear(); int taskbar_icon_shortcuts_x_offset = 0, taskbar_icon_shortcuts_y_offset = 0; - float current_background_anim_value = (float)background_animation.value(Animation::AnimFunctions::LINEAR); - float current_global_windows_shortcuts_anim_value = (float)global_windows_shortcuts_animation.value(Animation::AnimFunctions::LINEAR); - float pos_global_windows_shortcuts_anim_value = 1 - (float)global_windows_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); - float pos_taskbar_icon_shortcuts_anim_value = 1 - (float)taskbar_icon_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); + double current_background_anim_value = background_animation.value(Animation::AnimFunctions::LINEAR); + double current_global_windows_shortcuts_anim_value = global_windows_shortcuts_animation.value(Animation::AnimFunctions::LINEAR); + double pos_global_windows_shortcuts_anim_value = 1 - global_windows_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); + double pos_taskbar_icon_shortcuts_anim_value = 1 - taskbar_icon_shortcuts_animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); // Draw background SetLayeredWindowAttributes(hwnd, 0, static_cast(255 * current_background_anim_value), LWA_ALPHA); @@ -667,8 +667,8 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) D2D1_COLOR_F brushColor = light_mode ? D2D1::ColorF(1.0f, 1.0f, 1.0f, brush_opacity) : D2D1::ColorF(0, 0, 0, brush_opacity); winrt::check_hresult(d2d_device_context->CreateSolidColorBrush(brushColor, brush.put())); D2D1_RECT_F background_rect = {}; - background_rect.bottom = (float)window_height; - background_rect.right = (float)window_width; + background_rect.bottom = static_cast(window_height); + background_rect.right = static_cast(window_width); d2d_device_context->SetTransform(D2D1::Matrix3x2F::Identity()); d2d_device_context->FillRectangle(background_rect, brush.get()); @@ -680,26 +680,26 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) if (tasklist_buttons[0].x <= window_rect.left) { // taskbar on left - taskbar_icon_shortcuts_x_offset = (int)(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); + taskbar_icon_shortcuts_x_offset = static_cast(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); } if (tasklist_buttons[0].x >= window_rect.right) { // taskbar on right - taskbar_icon_shortcuts_x_offset = (int)(pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); + taskbar_icon_shortcuts_x_offset = static_cast(pos_taskbar_icon_shortcuts_anim_value * use_overlay->width() * use_overlay->get_scale()); } if (tasklist_buttons[0].y <= window_rect.top) { // taskbar on top - taskbar_icon_shortcuts_y_offset = (int)(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); + taskbar_icon_shortcuts_y_offset = static_cast(-pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); } if (tasklist_buttons[0].y >= window_rect.bottom) { // taskbar on bottom - taskbar_icon_shortcuts_y_offset = (int)(pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); + taskbar_icon_shortcuts_y_offset = static_cast(pos_taskbar_icon_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); } for (auto&& button : tasklist_buttons) { - if ((size_t)(button.keynum) - 1 >= arrows.size()) + if (static_cast(button.keynum) - 1 >= arrows.size()) { continue; } @@ -767,10 +767,10 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) RECT thumbnail_pos; if (render_monitors) { - thumbnail_pos.left = (int)((thumb_window->left + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - thumbnail_pos.top = (int)((thumb_window->top + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); - thumbnail_pos.right = (int)((thumb_window->right + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - thumbnail_pos.bottom = (int)((thumb_window->bottom + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + thumbnail_pos.left = static_cast((thumb_window->left + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + thumbnail_pos.top = static_cast((thumb_window->top + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + thumbnail_pos.right = static_cast((thumb_window->right + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + thumbnail_pos.bottom = static_cast((thumb_window->bottom + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); } else { @@ -791,17 +791,17 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) // render the monitors if (render_monitors) { - brushColor = D2D1::ColorF(colors.start_color_menu, miniature_shown ? current_global_windows_shortcuts_anim_value * 0.9f : current_global_windows_shortcuts_anim_value * 0.3f); + brushColor = D2D1::ColorF(colors.start_color_menu, miniature_shown ? static_cast(current_global_windows_shortcuts_anim_value * 0.9) : static_cast(current_global_windows_shortcuts_anim_value * 0.3)); brush = nullptr; winrt::check_hresult(d2d_device_context->CreateSolidColorBrush(brushColor, brush.put())); for (auto& monitor : monitors) { D2D1_RECT_F monitor_rect; const auto monitor_size = monitor.GetScreenSize(true); - monitor_rect.left = (float)((monitor_size.left() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - monitor_rect.top = (float)((monitor_size.top() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); - monitor_rect.right = (float)((monitor_size.right() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); - monitor_rect.bottom = (float)((monitor_size.bottom() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + monitor_rect.left = static_cast((monitor_size.left() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + monitor_rect.top = static_cast((monitor_size.top() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); + monitor_rect.right = static_cast((monitor_size.right() + monitor_dx) * rect_and_scale.scale + rect_and_scale.rect.left); + monitor_rect.bottom = static_cast((monitor_size.bottom() + monitor_dy) * rect_and_scale.scale + rect_and_scale.rect.top); d2d_device_context->SetTransform(D2D1::Matrix3x2F::Identity()); d2d_device_context->FillRectangle(monitor_rect, brush.get()); } @@ -815,8 +815,8 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) } // Set the animation - move the draw window according to animation step - int global_windows_shortcuts_y_offset = (int)(pos_global_windows_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); - auto popIn = D2D1::Matrix3x2F::Translation(0, (float)global_windows_shortcuts_y_offset); + int global_windows_shortcuts_y_offset = static_cast(pos_global_windows_shortcuts_anim_value * use_overlay->height() * use_overlay->get_scale()); + auto popIn = D2D1::Matrix3x2F::Translation(0, static_cast(global_windows_shortcuts_y_offset)); d2d_device_context->SetTransform(popIn); // Animate keys @@ -824,7 +824,7 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_device_context) { auto& animation = key_animations[id]; D2D1_COLOR_F color; - auto value = (float)animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO); + auto value = static_cast(animation.animation.value(Animation::AnimFunctions::EASE_OUT_EXPO)); color.a = 1.0f; color.r = animation.original.r + (1.0f - animation.original.r) * value; color.g = animation.original.g + (1.0f - animation.original.g) * value; diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp index 8cff1c4e47..f1c843d615 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.cpp @@ -115,12 +115,12 @@ namespace return true; } - bool isWin(int key) + constexpr bool isWin(int key) { return key == VK_LWIN || key == VK_RWIN; } - bool isKeyDown(LowlevelKeyboardEvent event) + constexpr bool isKeyDown(LowlevelKeyboardEvent event) { return event.wParam == WM_KEYDOWN || event.wParam == WM_SYSKEYDOWN; } @@ -160,6 +160,30 @@ namespace return CallNextHookEx(NULL, nCode, wParam, lParam); } + LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) + { + if (nCode >= 0) + { + switch (wParam) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + // Don't close with mouse click if activation is windows key and the key is pressed + if (!overlay_window_instance->win_key_activation() || !isWinPressed()) + { + overlay_window_instance->CloseWindow(HideWindowType::MOUSE_BUTTONUP); + } + break; + default: + break; + } + } + + return CallNextHookEx(0, nCode, wParam, lParam); + } + std::wstring ToWstring(HideWindowType type) { switch (type) @@ -172,6 +196,8 @@ namespace return L"WIN_SHORTCUT_PRESSED"; case HideWindowType::THE_SHORTCUT_PRESSED: return L"THE_SHORTCUT_PRESSED"; + case HideWindowType::MOUSE_BUTTONUP: + return L"MOUSE_BUTTONUP"; } return L""; @@ -191,12 +217,18 @@ OverlayWindow::OverlayWindow(HWND activeWindow) { Logger::warn(L"Failed to create low level keyboard hook. {}", get_last_error_or_default(GetLastError())); } + + mouseHook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, GetModuleHandle(NULL), NULL); + if (!mouseHook) + { + Logger::warn(L"Failed to create low level mouse hook. {}", get_last_error_or_default(GetLastError())); + } } void OverlayWindow::ShowWindow() { winkey_popup = std::make_unique(); - winkey_popup->apply_overlay_opacity(((float)overlayOpacity.value) / 100.0f); + winkey_popup->apply_overlay_opacity(overlayOpacity.value / 100.0f); winkey_popup->set_theme(theme.value); // The press time only takes effect when the shortcut guide is activated by pressing the win key. @@ -316,6 +348,11 @@ bool OverlayWindow::overlay_visible() const return target_state->active(); } +bool OverlayWindow::win_key_activation() const +{ + return shouldReactToPressedWinKey.value; +} + void OverlayWindow::init_settings() { auto settings = GetSettings(); @@ -336,7 +373,7 @@ bool OverlayWindow::is_disabled_app(wchar_t* exePath) } auto exePathUpper = std::wstring(exePath); - CharUpperBuffW(exePathUpper.data(), (DWORD)exePathUpper.length()); + CharUpperBuffW(exePathUpper.data(), static_cast(exePathUpper.length())); for (const auto& row : disabled_apps_array) { const auto pos = exePathUpper.rfind(row); @@ -354,7 +391,7 @@ void OverlayWindow::update_disabled_apps() { disabled_apps_array.clear(); auto disabledUppercase = disabledApps.value; - CharUpperBuffW(disabledUppercase.data(), (DWORD)disabledUppercase.length()); + CharUpperBuffW(disabledUppercase.data(), static_cast(disabledUppercase.length())); std::wstring_view view(disabledUppercase); view = trim(view); while (!view.empty()) @@ -418,7 +455,7 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept try { - settings.overlayOpacity = (int)properties.GetNamedObject(OverlayOpacity::name).GetNamedNumber(L"value"); + settings.overlayOpacity = static_cast(properties.GetNamedObject(OverlayOpacity::name).GetNamedNumber(L"value")); } catch (...) { @@ -434,7 +471,7 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept try { - settings.windowsKeyPressTimeForGlobalWindowsShortcuts = (int)properties.GetNamedObject(WindowsKeyPressTimeForGlobalWindowsShortcuts::name).GetNamedNumber(L"value"); + settings.windowsKeyPressTimeForGlobalWindowsShortcuts = static_cast(properties.GetNamedObject(WindowsKeyPressTimeForGlobalWindowsShortcuts::name).GetNamedNumber(L"value")); } catch (...) { @@ -442,7 +479,7 @@ ShortcutGuideSettings OverlayWindow::GetSettings() noexcept try { - settings.windowsKeyPressTimeForTaskbarIconShortcuts = (int)properties.GetNamedObject(WindowsKeyPressTimeForTaskbarIconShortcuts::name).GetNamedNumber(L"value"); + settings.windowsKeyPressTimeForTaskbarIconShortcuts = static_cast(properties.GetNamedObject(WindowsKeyPressTimeForTaskbarIconShortcuts::name).GetNamedNumber(L"value")); } catch (...) { diff --git a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h index 418ddd1862..57f9bf3267 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h +++ b/src/modules/ShortcutGuide/ShortcutGuide/shortcut_guide.h @@ -18,7 +18,8 @@ enum class HideWindowType ESC_PRESSED, WIN_RELEASED, WIN_SHORTCUT_PRESSED, - THE_SHORTCUT_PRESSED + THE_SHORTCUT_PRESSED, + MOUSE_BUTTONUP }; class OverlayWindow @@ -34,6 +35,7 @@ public: void was_hidden(); bool overlay_visible() const; + bool win_key_activation() const; bool is_disabled_app(wchar_t* exePath); @@ -52,6 +54,7 @@ private: void update_disabled_apps(); HWND activeWindow; HHOOK keyboardHook; + HHOOK mouseHook; struct OverlayOpacity { diff --git a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp index af77a03ab2..687fc86566 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/tasklist_positions.cpp @@ -60,16 +60,16 @@ bool Tasklist::update_buttons(std::vector& buttons) double value; pos = 0; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.x = (long)value; + button.x = static_cast(value); pos = 1; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.y = (long)value; + button.y = static_cast(value); pos = 2; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.width = (long)value; + button.width = static_cast(value); pos = 3; SafeArrayGetElement(var_rect.parray, &pos, &value); - button.height = (long)value; + button.height = static_cast(value); } VariantClear(&var_rect); } diff --git a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp index a7d7a1cc1b..297b5ab4bc 100644 --- a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/dllmain.cpp @@ -10,6 +10,7 @@ #include "../interface/powertoy_module_interface.h" #include "Generated Files/resource.h" #include +#include BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD /*ul_reason_for_call*/, LPVOID /*lpReserved*/) { @@ -35,6 +36,11 @@ public: Logger::warn(L"Failed to create {} event. {}", CommonSharedConstants::SHORTCUT_GUIDE_EXIT_EVENT, get_last_error_or_default(GetLastError())); } + triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT); + triggerEventWaiter = EventWaiter(CommonSharedConstants::SHORTCUT_GUIDE_TRIGGER_EVENT, [this](int) { + OnHotkeyEx(); + }); + InitSettings(); } @@ -191,7 +197,9 @@ private: UINT m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_GLOBAL_WINDOWS_SHORTCUTS; UINT m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = DEFAULT_MILLISECONDS_WIN_KEY_PRESS_TIME_FOR_TASKBAR_ICON_SHORTCUTS; + HANDLE triggerEvent; HANDLE exitEvent; + EventWaiter triggerEventWaiter; bool StartProcess(std::wstring args = L"") { @@ -324,11 +332,27 @@ private: { // Parse Legacy windows key press behavior settings auto jsonUseLegacyWinKeyBehaviorObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"use_legacy_press_win_key_behavior"); - m_shouldReactToPressedWinKey = (bool)jsonUseLegacyWinKeyBehaviorObject.GetNamedBoolean(L"value"); + m_shouldReactToPressedWinKey = jsonUseLegacyWinKeyBehaviorObject.GetNamedBoolean(L"value"); auto jsonPressTimeForGlobalWindowsShortcutsObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time"); auto jsonPressTimeForTaskbarIconShortcutsObject = settingsObject.GetNamedObject(L"properties").GetNamedObject(L"press_time_for_taskbar_icon_shortcuts"); - m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = (UINT)jsonPressTimeForGlobalWindowsShortcutsObject.GetNamedNumber(L"value"); - m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = (UINT)jsonPressTimeForTaskbarIconShortcutsObject.GetNamedNumber(L"value"); + int value = static_cast(jsonPressTimeForGlobalWindowsShortcutsObject.GetNamedNumber(L"value")); + if (value >= 0) + { + m_millisecondsWinKeyPressTimeForGlobalWindowsShortcuts = value; + } + else + { + throw; + } + value = static_cast(jsonPressTimeForTaskbarIconShortcutsObject.GetNamedNumber(L"value")); + if (value >= 0) + { + m_millisecondsWinKeyPressTimeForTaskbarIconShortcuts = value; + } + else + { + throw; + } } catch (...) { diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp index 9f9db964f5..b61b75ea8d 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp @@ -23,7 +23,7 @@ namespace NonLocalizable bool isExcluded(HWND window) { auto processPath = get_process_path(window); - CharUpperBuffW(processPath.data(), (DWORD)processPath.length()); + CharUpperBuffW(processPath.data(), static_cast(processPath.length())); return find_app_name_in_path(processPath, AlwaysOnTopSettings::settings().excludedApps); } @@ -381,7 +381,7 @@ bool AlwaysOnTop::IsPinned(HWND window) const noexcept bool AlwaysOnTop::PinTopmostWindow(HWND window) const noexcept { - if (!SetProp(window, NonLocalizable::WINDOW_IS_PINNED_PROP, (HANDLE)1)) + if (!SetProp(window, NonLocalizable::WINDOW_IS_PINNED_PROP, reinterpret_cast(1))) { Logger::error(L"SetProp failed, {}", get_last_error_or_default(GetLastError())); } diff --git a/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp b/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp index 9613769f2f..3978f3d105 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/FrameDrawer.cpp @@ -188,10 +188,10 @@ D2D1_ROUNDED_RECT FrameDrawer::ConvertRect(RECT rect, int thickness, float radiu float halfThickness = thickness / 2.0f; // 1 is needed to eliminate the gap between border and window - auto d2d1Rect = D2D1::RectF((float)rect.left + halfThickness + 1, - (float)rect.top + halfThickness + 1, - (float)rect.right - halfThickness - 1, - (float)rect.bottom - halfThickness - 1); + auto d2d1Rect = D2D1::RectF(static_cast(rect.left) + halfThickness + 1, + static_cast(rect.top) + halfThickness + 1, + static_cast(rect.right) - halfThickness - 1, + static_cast(rect.bottom) - halfThickness - 1); return D2D1::RoundedRect(d2d1Rect, radius, radius); } @@ -200,10 +200,10 @@ D2D1_RECT_F FrameDrawer::ConvertRect(RECT rect, int thickness) float halfThickness = thickness / 2.0f; // 1 is needed to eliminate the gap between border and window - return D2D1::RectF((float)rect.left + halfThickness + 1, - (float)rect.top + halfThickness + 1, - (float)rect.right - halfThickness - 1, - (float)rect.bottom - halfThickness - 1); + return D2D1::RectF(static_cast(rect.left) + halfThickness + 1, + static_cast(rect.top) + halfThickness + 1, + static_cast(rect.right) - halfThickness - 1, + static_cast(rect.bottom) - halfThickness - 1); } void FrameDrawer::Render() diff --git a/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp b/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp index f91ce253a8..251fa1f6ba 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/Settings.cpp @@ -169,7 +169,7 @@ void AlwaysOnTopSettings::LoadSettings() std::wstring apps = std::move(*jsonVal); std::vector excludedApps; auto excludedUppercase = apps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); diff --git a/src/modules/awake/Awake/Awake.csproj b/src/modules/awake/Awake/Awake.csproj index 01da46c13a..34c720a427 100644 --- a/src/modules/awake/Awake/Awake.csproj +++ b/src/modules/awake/Awake/Awake.csproj @@ -56,18 +56,19 @@ - + all - - - - - + + + + + + diff --git a/src/modules/awake/Awake/Core/APIHelper.cs b/src/modules/awake/Awake/Core/APIHelper.cs index 91aff0e629..f0f303db1e 100644 --- a/src/modules/awake/Awake/Core/APIHelper.cs +++ b/src/modules/awake/Awake/Core/APIHelper.cs @@ -6,6 +6,8 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reactive.Concurrency; +using System.Reactive.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -82,31 +84,54 @@ namespace Awake.Core } } - public static void SetIndefiniteKeepAwake(Action callback, Action failureCallback, bool keepDisplayOn = false) + private static bool SetAwakeStateBasedOnDisplaySetting(bool keepDisplayOn) { - PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeIndefinitelyKeepAwakeEvent()); + if (keepDisplayOn) + { + return SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + } + else + { + return SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + } + } + public static void CancelExistingThread() + { _tokenSource.Cancel(); try { + _log.Info("Attempting to ensure that the thread is properly cleaned up..."); + if (_runnerThread != null && !_runnerThread.IsCanceled) { _runnerThread.Wait(_threadToken); } + + _log.Info("Thread is clean."); } catch (OperationCanceledException) { - _log.Info("Confirmed background thread cancellation when setting indefinite keep awake."); + _log.Info("Confirmed background thread cancellation when disabling explicit keep awake."); } _tokenSource = new CancellationTokenSource(); _threadToken = _tokenSource.Token; + _log.Info("Instantiating of new token source and thread token completed."); + } + + public static void SetIndefiniteKeepAwake(Action callback, Action failureCallback, bool keepDisplayOn = false) + { + PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeIndefinitelyKeepAwakeEvent()); + + CancelExistingThread(); + try { - _runnerThread = Task.Run(() => RunIndefiniteLoop(keepDisplayOn), _threadToken) - .ContinueWith((result) => callback(result.Result), TaskContinuationOptions.OnlyOnRanToCompletion) + _runnerThread = Task.Run(() => RunIndefiniteJob(keepDisplayOn), _threadToken) + .ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion) .ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion); } catch (Exception ex) @@ -117,80 +142,101 @@ namespace Awake.Core public static void SetNoKeepAwake() { - _tokenSource.Cancel(); + PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeNoKeepAwakeEvent()); - try - { - if (_runnerThread != null && !_runnerThread.IsCanceled) - { - _runnerThread.Wait(_threadToken); - } - } - catch (OperationCanceledException) - { - _log.Info("Confirmed background thread cancellation when disabling explicit keep awake."); - } + CancelExistingThread(); } - public static void SetTimedKeepAwake(uint seconds, Action callback, Action failureCallback, bool keepDisplayOn = true) + public static void SetExpirableKeepAwake(DateTimeOffset expireAt, Action callback, Action failureCallback, bool keepDisplayOn = true) { - PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeTimedKeepAwakeEvent()); + PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeExpirableKeepAwakeEvent()); - _tokenSource.Cancel(); + CancelExistingThread(); - try + if (expireAt > DateTime.Now && expireAt != null) { - if (_runnerThread != null && !_runnerThread.IsCanceled) - { - _runnerThread.Wait(_threadToken); - } - } - catch (OperationCanceledException) - { - _log.Info("Confirmed background thread cancellation when setting timed keep awake."); - } - - _tokenSource = new CancellationTokenSource(); - _threadToken = _tokenSource.Token; - - _runnerThread = Task.Run(() => RunTimedLoop(seconds, keepDisplayOn), _threadToken) - .ContinueWith((result) => callback(result.Result), TaskContinuationOptions.OnlyOnRanToCompletion) - .ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion); - } - - private static bool RunIndefiniteLoop(bool keepDisplayOn = false) - { - bool success; - if (keepDisplayOn) - { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + _runnerThread = Task.Run(() => RunExpiringJob(expireAt, keepDisplayOn), _threadToken) + .ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion) + .ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion); } else { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); + // The target date is not in the future. + _log.Error("The specified target date and time is not in the future."); + _log.Error($"Current time: {DateTime.Now}\tTarget time: {expireAt}"); } + } + + public static void SetTimedKeepAwake(uint seconds, Action callback, Action failureCallback, bool keepDisplayOn = true) + { + PowerToysTelemetry.Log.WriteEvent(new Awake.Telemetry.AwakeTimedKeepAwakeEvent()); + + CancelExistingThread(); + + _runnerThread = Task.Run(() => RunTimedJob(seconds, keepDisplayOn), _threadToken) + .ContinueWith((result) => callback, TaskContinuationOptions.OnlyOnRanToCompletion) + .ContinueWith((result) => failureCallback, TaskContinuationOptions.NotOnRanToCompletion); + } + + private static void RunExpiringJob(DateTimeOffset expireAt, bool keepDisplayOn = false) + { + bool success = false; + + // In case cancellation was already requested. + _threadToken.ThrowIfCancellationRequested(); try { + success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn); + if (success) { - _log.Info($"Initiated indefinite keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}"); + _log.Info($"Initiated expirable keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}"); - WaitHandle.WaitAny(new[] { _threadToken.WaitHandle }); - - return success; + Observable.Timer(expireAt, Scheduler.CurrentThread).Subscribe( + _ => + { + _log.Info($"Completed expirable thread in {PInvoke.GetCurrentThreadId()}."); + CancelExistingThread(); + }, + _tokenSource.Token); } else { - _log.Info("Could not successfully set up indefinite keep awake."); - return success; + _log.Info("Could not successfully set up expirable keep awake."); + } + } + catch (OperationCanceledException ex) + { + // Task was clearly cancelled. + _log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}"); + } + } + + private static void RunIndefiniteJob(bool keepDisplayOn = false) + { + // In case cancellation was already requested. + _threadToken.ThrowIfCancellationRequested(); + + try + { + bool success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn); + + if (success) + { + _log.Info($"Initiated indefinite keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}"); + + WaitHandle.WaitAny(new[] { _threadToken.WaitHandle }); + } + else + { + _log.Info("Could not successfully set up indefinite keep awake."); } } catch (OperationCanceledException ex) { // Task was clearly cancelled. _log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}"); - return success; } } @@ -221,59 +267,38 @@ namespace Awake.Core } } - private static bool RunTimedLoop(uint seconds, bool keepDisplayOn = true) + private static void RunTimedJob(uint seconds, bool keepDisplayOn = true) { bool success = false; // In case cancellation was already requested. _threadToken.ThrowIfCancellationRequested(); + try { - if (keepDisplayOn) - { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); - } - else - { - success = SetAwakeState(EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS); - } + success = SetAwakeStateBasedOnDisplaySetting(keepDisplayOn); if (success) { - _log.Info($"Initiated temporary keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}"); + _log.Info($"Initiated timed keep awake in background thread: {PInvoke.GetCurrentThreadId()}. Screen on: {keepDisplayOn}"); - _timedLoopTimer = new System.Timers.Timer((seconds * 1000) + 1); - _timedLoopTimer.Elapsed += (s, e) => - { - _tokenSource.Cancel(); - - _timedLoopTimer.Stop(); - }; - - _timedLoopTimer.Disposed += (s, e) => - { - _log.Info("Old timer disposed."); - }; - - _timedLoopTimer.Start(); - - WaitHandle.WaitAny(new[] { _threadToken.WaitHandle }); - _timedLoopTimer.Stop(); - _timedLoopTimer.Dispose(); - - return success; + Observable.Timer(TimeSpan.FromSeconds(seconds), Scheduler.CurrentThread).Subscribe( + _ => + { + _log.Info($"Completed timed thread in {PInvoke.GetCurrentThreadId()}."); + CancelExistingThread(); + }, + _tokenSource.Token); } else { _log.Info("Could not set up timed keep-awake with display on."); - return success; } } catch (OperationCanceledException ex) { // Task was clearly cancelled. _log.Info($"Background thread termination: {PInvoke.GetCurrentThreadId()}. Message: {ex.Message}"); - return success; } } @@ -357,10 +382,12 @@ namespace Awake.Core public static Dictionary GetDefaultTrayOptions() { - Dictionary optionsList = new Dictionary(); - optionsList.Add("30 minutes", 1800); - optionsList.Add("1 hour", 3600); - optionsList.Add("2 hours", 7200); + Dictionary optionsList = new Dictionary + { + { "30 minutes", 1800 }, + { "1 hour", 3600 }, + { "2 hours", 7200 }, + }; return optionsList; } } diff --git a/src/modules/awake/Awake/Core/Models/ControlType.cs b/src/modules/awake/Awake/Core/Models/ControlType.cs deleted file mode 100644 index c7b37894cc..0000000000 --- a/src/modules/awake/Awake/Core/Models/ControlType.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -namespace Awake.Core.Models -{ - // See: https://learn.microsoft.com/windows/console/handlerroutine - public enum ControlType - { - CTRL_C_EVENT = 0, - CTRL_BREAK_EVENT = 1, - CTRL_CLOSE_EVENT = 2, - CTRL_LOGOFF_EVENT = 5, - CTRL_SHUTDOWN_EVENT = 6, - } -} diff --git a/src/modules/awake/Awake/Core/Models/TrayCommands.cs b/src/modules/awake/Awake/Core/Models/TrayCommands.cs index cca274fd5f..041db7834e 100644 --- a/src/modules/awake/Awake/Core/Models/TrayCommands.cs +++ b/src/modules/awake/Awake/Core/Models/TrayCommands.cs @@ -11,7 +11,8 @@ namespace Awake.Core.Models TC_DISPLAY_SETTING = PInvoke.WM_USER + 1, TC_MODE_PASSIVE = PInvoke.WM_USER + 2, TC_MODE_INDEFINITE = PInvoke.WM_USER + 3, - TC_EXIT = PInvoke.WM_USER + 4, - TC_TIME = PInvoke.WM_USER + 5, + TC_MODE_EXPIRABLE = PInvoke.WM_USER + 4, + TC_EXIT = PInvoke.WM_USER + 100, + TC_TIME = PInvoke.WM_USER + 101, } } diff --git a/src/modules/awake/Awake/Core/TrayHelper.cs b/src/modules/awake/Awake/Core/TrayHelper.cs index 171da89ff2..25f08b7855 100644 --- a/src/modules/awake/Awake/Core/TrayHelper.cs +++ b/src/modules/awake/Awake/Core/TrayHelper.cs @@ -20,6 +20,13 @@ using Windows.Win32.UI.WindowsAndMessaging; namespace Awake.Core { + /// + /// Helper class used to manage the system tray. + /// + /// + /// Because Awake is a console application, there is no built-in + /// way to embed UI components so we have to heavily rely on the native Windows API. + /// internal static class TrayHelper { private static readonly Logger _log; @@ -89,7 +96,7 @@ namespace Awake.Core text, settings.Properties.KeepDisplayOn, settings.Properties.Mode, - settings.Properties.TrayTimeShortcuts, + settings.Properties.CustomTrayTimes, startedFromPowerToys); } @@ -106,7 +113,7 @@ namespace Awake.Core PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty); } - PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (keepDisplayOn ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED) | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_DISABLED : MENU_ITEM_FLAGS.MF_ENABLED), (uint)TrayCommands.TC_DISPLAY_SETTING, "Keep screen on"); } // In case there are no tray shortcuts defined for the application default to a @@ -116,19 +123,18 @@ namespace Awake.Core trayTimeShortcuts.AddRange(APIHelper.GetDefaultTrayOptions()); } - // TODO: Make sure that this loads from JSON instead of being hard-coded. var awakeTimeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false); for (int i = 0; i < trayTimeShortcuts.Count; i++) { PInvoke.InsertMenu(awakeTimeMenu, (uint)i, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING, (uint)TrayCommands.TC_TIME + (uint)i, trayTimeShortcuts.ElementAt(i).Key); } - var modeMenu = new DestroyMenuSafeHandle(PInvoke.CreatePopupMenu(), false); - PInvoke.InsertMenu(modeMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)"); - PInvoke.InsertMenu(modeMenu, 1, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.INDEFINITE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_SEPARATOR, 0, string.Empty); - PInvoke.InsertMenu(modeMenu, 2, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP | (mode == AwakeMode.TIMED ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)awakeTimeMenu.DangerousGetHandle(), "Keep awake temporarily"); - PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP, (uint)modeMenu.DangerousGetHandle(), "Mode"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.PASSIVE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_PASSIVE, "Off (keep using the selected power plan)"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | (mode == AwakeMode.INDEFINITE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_INDEFINITE, "Keep awake indefinitely"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_POPUP | (mode == AwakeMode.TIMED ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)awakeTimeMenu.DangerousGetHandle(), "Keep awake on interval"); + PInvoke.InsertMenu(TrayMenu, 0, MENU_ITEM_FLAGS.MF_BYPOSITION | MENU_ITEM_FLAGS.MF_STRING | MENU_ITEM_FLAGS.MF_DISABLED | (mode == AwakeMode.EXPIRABLE ? MENU_ITEM_FLAGS.MF_CHECKED : MENU_ITEM_FLAGS.MF_UNCHECKED), (uint)TrayCommands.TC_MODE_EXPIRABLE, "Keep awake until expiration date and time"); TrayIcon.Text = text; } diff --git a/src/modules/awake/Awake/Core/TrayMessageFilter.cs b/src/modules/awake/Awake/Core/TrayMessageFilter.cs index 06e07e1031..f7e86d71fa 100644 --- a/src/modules/awake/Awake/Core/TrayMessageFilter.cs +++ b/src/modules/awake/Awake/Core/TrayMessageFilter.cs @@ -56,13 +56,13 @@ namespace Awake.Core // Format for the timer block: // TrayCommands.TC_TIME + ZERO_BASED_INDEX_IN_SETTINGS AwakeSettings settings = ModuleSettings.GetSettings(InternalConstants.AppName); - if (settings.Properties.TrayTimeShortcuts.Count == 0) + if (settings.Properties.CustomTrayTimes.Count == 0) { - settings.Properties.TrayTimeShortcuts.AddRange(APIHelper.GetDefaultTrayOptions()); + settings.Properties.CustomTrayTimes.AddRange(APIHelper.GetDefaultTrayOptions()); } int index = (int)targetCommandIndex - (int)TrayCommands.TC_TIME; - var targetTime = settings.Properties.TrayTimeShortcuts.ElementAt(index).Value; + var targetTime = settings.Properties.CustomTrayTimes.ElementAt(index).Value; TimedKeepAwakeCommandHandler(InternalConstants.AppName, targetTime); break; } @@ -112,8 +112,8 @@ namespace Awake.Core } currentSettings.Properties.Mode = AwakeMode.TIMED; - currentSettings.Properties.Hours = (uint)timeSpan.Hours; - currentSettings.Properties.Minutes = (uint)timeSpan.Minutes; + currentSettings.Properties.IntervalHours = (uint)timeSpan.Hours; + currentSettings.Properties.IntervalMinutes = (uint)timeSpan.Minutes; ModuleSettings.SaveSettings(JsonSerializer.Serialize(currentSettings), moduleName); } diff --git a/src/modules/awake/Awake/NLog.config b/src/modules/awake/Awake/NLog.config index 13d89675d0..aaa21c75c5 100644 --- a/src/modules/awake/Awake/NLog.config +++ b/src/modules/awake/Awake/NLog.config @@ -2,7 +2,7 @@ - + ( + Option configOption = new( aliases: new[] { "--use-pt-config", "-c" }, getDefaultValue: () => false, description: $"Specifies whether {InternalConstants.AppName} will be using the PowerToys configuration file for managing the state.") @@ -106,11 +108,10 @@ namespace Awake { Arity = ArgumentArity.ZeroOrOne, }, + Required = false, }; - configOption.Required = false; - - var displayOption = new Option( + Option displayOption = new( aliases: new[] { "--display-on", "-d" }, getDefaultValue: () => true, description: "Determines whether the display should be kept awake.") @@ -119,11 +120,10 @@ namespace Awake { Arity = ArgumentArity.ZeroOrOne, }, + Required = false, }; - displayOption.Required = false; - - var timeOption = new Option( + Option timeOption = new( aliases: new[] { "--time-limit", "-t" }, getDefaultValue: () => 0, description: "Determines the interval, in seconds, during which the computer is kept awake.") @@ -132,34 +132,45 @@ namespace Awake { Arity = ArgumentArity.ExactlyOne, }, + Required = false, }; - timeOption.Required = false; - - var pidOption = new Option( + Option pidOption = new( aliases: new[] { "--pid", "-p" }, getDefaultValue: () => 0, - description: $"Bind the execution of {InternalConstants.AppName} to another process.") + description: $"Bind the execution of {InternalConstants.AppName} to another process. When the process ends, the system will resume managing the current sleep/display mode.") { Argument = new Argument(() => 0) { Arity = ArgumentArity.ZeroOrOne, }, + Required = false, }; - pidOption.Required = false; + Option expireAtOption = new( + aliases: new[] { "--expire-at", "-e" }, + getDefaultValue: () => string.Empty, + description: $"Determines the end date/time when {InternalConstants.AppName} will back off and let the system manage the current sleep/display mode.") + { + Argument = new Argument(() => string.Empty) + { + Arity = ArgumentArity.ZeroOrOne, + }, + Required = false, + }; - RootCommand? rootCommand = new RootCommand + RootCommand? rootCommand = new() { configOption, displayOption, timeOption, pidOption, + expireAtOption, }; rootCommand.Description = InternalConstants.AppName; - rootCommand.Handler = CommandHandler.Create(HandleCommandLineArguments); + rootCommand.Handler = CommandHandler.Create(HandleCommandLineArguments); _log.Info("Parameter setup complete. Proceeding to the rest of the app initiation..."); @@ -180,7 +191,7 @@ namespace Awake APIHelper.CompleteExit(exitCode, exitSignal, force); } - private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid) + private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt) { _handler += ExitHandler; APIHelper.SetConsoleControlHandler(_handler, true); @@ -199,6 +210,7 @@ namespace Awake _log.Info($"The value for --display-on is: {displayOn}"); _log.Info($"The value for --time-limit is: {timeLimit}"); _log.Info($"The value for --pid is: {pid}"); + _log.Info($"The value for --expire is: {expireAt}"); if (usePtConfig) { @@ -214,41 +226,21 @@ namespace Awake Exit("Received a signal to end the process. Making sure we quit...", 0, _exitSignal, true); } }).Start(); - TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon("modules/awake/images/awake.ico"), _exitSignal); + + TrayHelper.InitializeTray(InternalConstants.FullAppName, new Icon(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images/awake.ico")), _exitSignal); string? settingsPath = _settingsUtils.GetSettingsFilePath(InternalConstants.AppName); _log.Info($"Reading configuration file: {settingsPath}"); - _watcher = new FileSystemWatcher + if (!File.Exists(settingsPath)) { -#pragma warning disable CS8601 // Possible null reference assignment. - Path = Path.GetDirectoryName(settingsPath), -#pragma warning restore CS8601 // Possible null reference assignment. - EnableRaisingEvents = true, - NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime, - Filter = Path.GetFileName(settingsPath), - }; + string? errorString = $"The settings file does not exist. Scaffolding default configuration..."; - IObservable>? changedObservable = Observable.FromEventPattern( - h => _watcher.Changed += h, - h => _watcher.Changed -= h); + AwakeSettings scaffoldSettings = new AwakeSettings(); + _settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), InternalConstants.AppName); + } - IObservable>? createdObservable = Observable.FromEventPattern( - cre => _watcher.Created += cre, - cre => _watcher.Created -= cre); - - IObservable>? mergedObservable = Observable.Merge(changedObservable, createdObservable); - - mergedObservable.Throttle(TimeSpan.FromMilliseconds(25)) - .SubscribeOn(TaskPoolScheduler.Default) - .Select(e => e.EventArgs) - .Subscribe(HandleAwakeConfigChange); - - TrayHelper.SetTray(InternalConstants.FullAppName, new AwakeSettings(), _startedFromPowerToys); - - // Initially the file might not be updated, so we need to start processing - // settings right away. - ProcessSettings(); + ScaffoldConfiguration(settingsPath); } catch (Exception ex) { @@ -259,15 +251,43 @@ namespace Awake } else { - AwakeMode mode = timeLimit <= 0 ? AwakeMode.INDEFINITE : AwakeMode.TIMED; - - if (mode == AwakeMode.INDEFINITE) + // Date-based binding takes precedence over timed configuration, so we want to + // check for that first. + if (!string.IsNullOrWhiteSpace(expireAt)) { - SetupIndefiniteKeepAwake(displayOn); + try + { + DateTime expirationDateTime = DateTime.Parse(expireAt, CultureInfo.CurrentCulture); + if (expirationDateTime > DateTime.Now) + { + // We want to have a dedicated expirable keep-awake logic instead of + // converting the target date to seconds and then passing to SetupTimedKeepAwake + // because that way we're accounting for the user potentially changing their clock + // while Awake is running. + SetupExpirableKeepAwake(expirationDateTime, displayOn); + } + else + { + _log.Info($"Target date is not in the future, therefore there is nothing to wait for."); + } + } + catch + { + _log.Error($"Could not parse date string {expireAt} into a viable date."); + } } else { - SetupTimedKeepAwake(timeLimit, displayOn); + AwakeMode mode = timeLimit <= 0 ? AwakeMode.INDEFINITE : AwakeMode.TIMED; + + if (mode == AwakeMode.INDEFINITE) + { + SetupIndefiniteKeepAwake(displayOn); + } + else + { + SetupTimedKeepAwake(timeLimit, displayOn); + } } } @@ -283,6 +303,45 @@ namespace Awake _exitSignal.WaitOne(); } + private static void ScaffoldConfiguration(string settingsPath) + { + try + { + _watcher = new FileSystemWatcher + { + Path = Path.GetDirectoryName(settingsPath)!, + EnableRaisingEvents = true, + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime, + Filter = Path.GetFileName(settingsPath), + }; + + IObservable>? changedObservable = Observable.FromEventPattern( + h => _watcher.Changed += h, + h => _watcher.Changed -= h); + + IObservable>? createdObservable = Observable.FromEventPattern( + cre => _watcher.Created += cre, + cre => _watcher.Created -= cre); + + IObservable>? mergedObservable = Observable.Merge(changedObservable, createdObservable); + + mergedObservable.Throttle(TimeSpan.FromMilliseconds(25)) + .SubscribeOn(TaskPoolScheduler.Default) + .Select(e => e.EventArgs) + .Subscribe(HandleAwakeConfigChange); + + TrayHelper.SetTray(InternalConstants.FullAppName, new AwakeSettings(), _startedFromPowerToys); + + // Initially the file might not be updated, so we need to start processing + // settings right away. + ProcessSettings(); + } + catch (Exception ex) + { + _log.Error($"An error occurred scaffolding the configuration. Error details: {ex.Message}"); + } + } + private static void SetupIndefiniteKeepAwake(bool displayOn) { APIHelper.SetIndefiniteKeepAwake(LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn); @@ -303,7 +362,7 @@ namespace Awake if (settings != null) { - _log.Info($"Identified custom time shortcuts for the tray: {settings.Properties.TrayTimeShortcuts.Count}"); + _log.Info($"Identified custom time shortcuts for the tray: {settings.Properties.CustomTrayTimes.Count}"); switch (settings.Properties.Mode) { @@ -321,12 +380,19 @@ namespace Awake case AwakeMode.TIMED: { - uint computedTime = (settings.Properties.Hours * 60 * 60) + (settings.Properties.Minutes * 60); + uint computedTime = (settings.Properties.IntervalHours * 60 * 60) + (settings.Properties.IntervalMinutes * 60); SetupTimedKeepAwake(computedTime, settings.Properties.KeepDisplayOn); break; } + case AwakeMode.EXPIRABLE: + { + SetupExpirableKeepAwake(settings.Properties.ExpirationDateTime, settings.Properties.KeepDisplayOn); + + break; + } + default: { string? errorMessage = "Unknown mode of operation. Check config file."; @@ -360,6 +426,13 @@ namespace Awake APIHelper.SetNoKeepAwake(); } + private static void SetupExpirableKeepAwake(DateTimeOffset expireAt, bool displayOn) + { + _log.Info($"Expirable keep-awake. Expected expiration date/time: {expireAt} with display on setting set to {displayOn}."); + + APIHelper.SetExpirableKeepAwake(expireAt, LogCompletedKeepAwakeThread, LogUnexpectedOrCancelledKeepAwakeThreadCompletion, displayOn); + } + private static void SetupTimedKeepAwake(uint time, bool displayOn) { _log.Info($"Timed keep-awake. Expected runtime: {time} seconds with display on setting set to {displayOn}."); @@ -369,14 +442,14 @@ namespace Awake private static void LogUnexpectedOrCancelledKeepAwakeThreadCompletion() { - string? errorMessage = "The keep-awake thread was terminated early."; + string? errorMessage = "The keep awake thread was terminated early."; _log.Info(errorMessage); _log.Debug(errorMessage); } - private static void LogCompletedKeepAwakeThread(bool result) + private static void LogCompletedKeepAwakeThread() { - _log.Info($"Exited keep-awake thread successfully: {result}"); + _log.Info($"Exited keep awake thread successfully."); } } } diff --git a/src/modules/awake/Awake/Telemetry/AwakeExpirableKeepAwakeEvent.cs b/src/modules/awake/Awake/Telemetry/AwakeExpirableKeepAwakeEvent.cs new file mode 100644 index 0000000000..114183dca0 --- /dev/null +++ b/src/modules/awake/Awake/Telemetry/AwakeExpirableKeepAwakeEvent.cs @@ -0,0 +1,16 @@ +// 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. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Awake.Telemetry +{ + [EventData] + public class AwakeExpirableKeepAwakeEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs b/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs new file mode 100644 index 0000000000..be52e82377 --- /dev/null +++ b/src/modules/awake/Awake/Telemetry/AwakeNoKeepAwakeEvent.cs @@ -0,0 +1,16 @@ +// 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. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Awake.Telemetry +{ + [EventData] + internal sealed class AwakeNoKeepAwakeEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/App.manifest b/src/modules/colorPicker/ColorPickerUI/App.manifest index 2f83c659bd..94b792d3fe 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.manifest +++ b/src/modules/colorPicker/ColorPickerUI/App.manifest @@ -40,7 +40,7 @@ - + diff --git a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs index 2a448c50e2..9097ec9c29 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs @@ -6,7 +6,6 @@ using System; using System.ComponentModel.Composition; using System.Threading; using System.Windows; -using ColorPicker.Helpers; using ColorPicker.Mouse; using Common.UI; using ManagedCommon; diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs index d8e1eb85f9..41f27ea3eb 100644 --- a/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/ChangeWindowPositionBehavior.cs @@ -5,6 +5,7 @@ using System.Windows; using ColorPicker.Helpers; using ColorPicker.Mouse; +using ManagedCommon; using Microsoft.Xaml.Behaviors; namespace ColorPicker.Behaviors diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index d75d01efee..14bdd57df1 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -49,12 +49,12 @@ - - - - - - + + + + + + @@ -70,6 +70,7 @@ + diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs index de9847ceb0..fb10b99b75 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/AppStateHandler.cs @@ -9,6 +9,7 @@ using System.Windows.Interop; using ColorPicker.Settings; using ColorPicker.ViewModelContracts; using Common.UI; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace ColorPicker.Helpers diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs index 627bd382c2..1f87d19fa3 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ClipboardHelper.cs @@ -5,6 +5,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Windows; +using ManagedCommon; using static ColorPicker.NativeMethods; namespace ColorPicker.Helpers diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs deleted file mode 100644 index 93116cbaa7..0000000000 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/Logger.cs +++ /dev/null @@ -1,79 +0,0 @@ -// 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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace ColorPicker.Helpers -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "ColorPicker\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs index da21c094de..8a5838d265 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/SessionEventHelper.cs @@ -4,6 +4,7 @@ using System; using ColorPicker.Telemetry; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Telemetry; diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs index fbbb416b71..d8c93bf1da 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/CursorManager.cs @@ -4,7 +4,7 @@ using System; using System.IO.Abstractions; -using ColorPicker.Helpers; +using ManagedCommon; using Microsoft.Win32; namespace ColorPicker.Mouse diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs index d75e57a024..080ec9d35e 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseHook.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Input; using ColorPicker.Helpers; +using ManagedCommon; using static ColorPicker.NativeMethods; namespace ColorPicker.Mouse diff --git a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs index 517ef40684..526466dc4a 100644 --- a/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs +++ b/src/modules/colorPicker/ColorPickerUI/Mouse/MouseInfoProvider.cs @@ -18,7 +18,7 @@ namespace ColorPicker.Mouse [PartCreationPolicy(CreationPolicy.Shared)] public class MouseInfoProvider : IMouseInfoProvider { - private const int MousePullInfoIntervalInMs = 10; + private readonly double _mousePullInfoIntervalInMs; private readonly DispatcherTimer _timer = new DispatcherTimer(); private readonly MouseHook _mouseHook; private readonly IUserSettings _userSettings; @@ -29,7 +29,8 @@ namespace ColorPicker.Mouse [ImportingConstructor] public MouseInfoProvider(AppStateHandler appStateMonitor, IUserSettings userSettings) { - _timer.Interval = TimeSpan.FromMilliseconds(MousePullInfoIntervalInMs); + _mousePullInfoIntervalInMs = 1000.0 / GetMainDisplayRefreshRate(); + _timer.Interval = TimeSpan.FromMilliseconds(_mousePullInfoIntervalInMs); _timer.Tick += Timer_Tick; if (appStateMonitor != null) @@ -111,6 +112,22 @@ namespace ColorPicker.Mouse return (System.Windows.Point)lpPoint; } + private static double GetMainDisplayRefreshRate() + { + double refreshRate = 60.0; + + foreach (var monitor in MonitorResolutionHelper.AllMonitors) + { + if (monitor.IsPrimary && EnumDisplaySettingsW(monitor.Name, ENUM_CURRENT_SETTINGS, out DEVMODEW lpDevMode)) + { + refreshRate = (double)lpDevMode.dmDisplayFrequency; + break; + } + } + + return refreshRate; + } + private void AppStateMonitor_AppClosed(object sender, EventArgs e) { DisposeHook(); diff --git a/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs b/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs index d02a5e0416..e2f277a8b5 100644 --- a/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs +++ b/src/modules/colorPicker/ColorPickerUI/NativeMethods.cs @@ -22,6 +22,9 @@ namespace ColorPicker public const int KfAltdown = 0x2000; public const int LlkhfAltdown = KfAltdown >> 8; public const int MonitorinfofPrimary = 0x00000001; + public const int CCHDEVICENAME = 32; + public const int CCHFORMNAME = 32; + public const uint ENUM_CURRENT_SETTINGS = 4294967295; public const int VK_SHIFT = 0x10; public const int VK_CONTROL = 0x11; public const int VK_MENU = 0x12; @@ -57,6 +60,13 @@ namespace ColorPicker internal static extern bool EnumDisplayMonitors( HandleRef hdc, IntPtr rcClip, MonitorEnumProc lpfnEnum, IntPtr dwData); + [DllImport("user32.dll", SetLastError = false, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EnumDisplaySettingsW( + string lpszDeviceName, + uint iModeNum, + out DEVMODEW lpDevMode); + [DllImport("user32.dll")] internal static extern bool GetCursorPos(out PointInter lpPoint); @@ -131,6 +141,50 @@ namespace ColorPicker public char[] szDevice = new char[32]; } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct DEVMODEW + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] + public string dmDeviceName; + + public ushort dmSpecVersion; + public ushort dmDriverVersion; + public ushort dmSize; + public ushort dmDriverExtra; + public uint dmFields; + + public int dmPositionX; + public int dmPositionY; + public uint dmDisplayOrientation; + public uint dmDisplayFixedOutput; + + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] + public string dmFormName; + + public short dmLogPixels; + public uint dmBitsPerPel; + public uint dmPelsWidth; + public uint dmPelsHeight; + + public uint dmDisplayFlags; + public uint dmDisplayFrequency; + + public uint dmICMMethod; + public uint dmICMIntent; + public uint dmMediaType; + public uint dmDitherType; + public uint dmReserved1; + public uint dmReserved2; + public uint dmPanningWidth; + public uint dmPanningHeight; + } + [StructLayout(LayoutKind.Sequential)] internal struct LowLevelKeyboardInputEvent { diff --git a/src/modules/colorPicker/ColorPickerUI/Program.cs b/src/modules/colorPicker/ColorPickerUI/Program.cs index 9c97e8308b..b3e502171f 100644 --- a/src/modules/colorPicker/ColorPickerUI/Program.cs +++ b/src/modules/colorPicker/ColorPickerUI/Program.cs @@ -6,6 +6,7 @@ using System; using ColorPicker.Helpers; using ColorPicker.Mouse; using ColorPickerUI; +using ManagedCommon; namespace ColorPicker { @@ -16,6 +17,8 @@ namespace ColorPicker [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\ColorPicker\\Logs"); + _args = args; Logger.LogInfo($"Color Picker started with pid={Environment.ProcessId}"); diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index 175413865c..f7f815fd9f 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -10,6 +10,7 @@ using System.IO.Abstractions; using System.Linq; using System.Threading; using ColorPicker.Common; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso index db23c058d3..49856b6822 100644 Binary files a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso and b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso differ diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx index 20d4fd27e2..5411ecbbc3 100644 --- a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx +++ b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx @@ -1,11 +1,13 @@ -float2 mousePosition : register(C1); +float2 mousePosition : register(C1); float radius : register(C2); float squareSize : register(c3); float textureSize : register(c4); sampler2D inputSampler : register(S0); -float4 main(float2 uv : TEXCOORD) : COLOR +float4 main(float2 uv + : TEXCOORD) : + COLOR { // do not draw grid where the mouse is if (uv.x == mousePosition.y && uv.y == mousePosition.y) @@ -42,8 +44,8 @@ float4 main(float2 uv : TEXCOORD) : COLOR int2 topLeftRectangle = int2(mousePositionX - (mousePositionX % squareSize) - 1, mousePositionY - (mousePositionY % squareSize) - 1); // do not draw grid inside square even when grid (avoid drawing grid in that area later - if (((pixelPositionX >= topLeftRectangle.x + 1 && pixelPositionX <= topLeftRectangle.x + squareSize) && (pixelPositionY == topLeftRectangle.y + 1 || pixelPositionY == topLeftRectangle.y + squareSize)) || - ((pixelPositionY >= topLeftRectangle.y + 1 && pixelPositionY <= topLeftRectangle.y + squareSize) && (pixelPositionX == topLeftRectangle.x + 1 || pixelPositionX == topLeftRectangle.x + squareSize))) + if (((pixelPositionX >= topLeftRectangle.x + 1 && pixelPositionX <= topLeftRectangle.x + squareSize + 1) && (pixelPositionY == topLeftRectangle.y + 1 || pixelPositionY == topLeftRectangle.y + squareSize + 1)) || + ((pixelPositionY >= topLeftRectangle.y + 1 && pixelPositionY <= topLeftRectangle.y + squareSize + 1) && (pixelPositionX == topLeftRectangle.x + 1 || pixelPositionX == topLeftRectangle.x + squareSize + 1))) { return originalColor; } diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs index ae747b0d58..e09c494e36 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs @@ -246,9 +246,7 @@ namespace ColorPicker.ViewModels new ColorFormatModel() { FormatName = ColorRepresentationType.HEX.ToString(), -#pragma warning disable CA1304 // Specify CultureInfo - Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HEX.ToString()).ToLower(), -#pragma warning restore CA1304 // Specify CultureInfo + Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.HEX.ToString()).ToLowerInvariant(), }); _allColorRepresentations.Add( diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj index 1c0f56a393..3928e27d46 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj @@ -16,10 +16,10 @@ - - - - + + + + diff --git a/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp b/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp new file mode 100644 index 0000000000..469f06c10d --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/DraggingState.cpp @@ -0,0 +1,83 @@ +#include "pch.h" +#include "DraggingState.h" + +#include +#include + +DraggingState::DraggingState(const std::function& keyUpdateCallback) : + m_mouseState(false), + m_mouseHook(std::bind(&DraggingState::OnMouseDown, this)), + m_leftShiftKeyState(keyUpdateCallback), + m_rightShiftKeyState(keyUpdateCallback), + m_ctrlKeyState(keyUpdateCallback), + m_keyUpdateCallback(keyUpdateCallback) +{ +} + +void DraggingState::Enable() +{ + if (FancyZonesSettings::settings().mouseSwitch) + { + m_mouseHook.enable(); + } + + m_leftShiftKeyState.enable(); + m_rightShiftKeyState.enable(); + m_ctrlKeyState.enable(); +} + +void DraggingState::Disable() +{ + const bool leftShiftPressed = m_leftShiftKeyState.state(); + const bool rightShiftPressed = m_rightShiftKeyState.state(); + + if (FancyZonesSettings::settings().shiftDrag) + { + if (leftShiftPressed) + { + FancyZonesUtils::SwallowKey(VK_LSHIFT); + } + + if (rightShiftPressed) + { + FancyZonesUtils::SwallowKey(VK_RSHIFT); + } + } + + m_dragging = false; + m_mouseState = false; + + m_mouseHook.disable(); + m_leftShiftKeyState.disable(); + m_rightShiftKeyState.disable(); + m_ctrlKeyState.disable(); +} + +void DraggingState::UpdateDraggingState() noexcept +{ + // This updates m_dragEnabled depending on if the shift key is being held down + if (FancyZonesSettings::settings().shiftDrag) + { + m_dragging = ((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_mouseState); + } + else + { + m_dragging = !((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_mouseState); + } +} + +void DraggingState::OnMouseDown() +{ + m_mouseState = !m_mouseState; + m_keyUpdateCallback(); +} + +bool DraggingState::IsDragging() const noexcept +{ + return m_dragging; +} + +bool DraggingState::IsSelectManyZonesState() const noexcept +{ + return m_ctrlKeyState.state(); +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/DraggingState.h b/src/modules/fancyzones/FancyZonesLib/DraggingState.h new file mode 100644 index 0000000000..cb6873a1be --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/DraggingState.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +class DraggingState +{ +public: + DraggingState(const std::function& keyUpdateCallback); + ~DraggingState() = default; + + void Enable(); + void Disable(); + void UpdateDraggingState() noexcept; + + bool IsDragging() const noexcept; + bool IsSelectManyZonesState() const noexcept; + +private: + void OnMouseDown(); + + std::atomic m_mouseState; + SecondaryMouseButtonsHook m_mouseHook; + KeyState m_leftShiftKeyState; + KeyState m_rightShiftKeyState; + KeyState m_ctrlKeyState; + std::function m_keyUpdateCallback; + + bool m_dragging{}; // True if we should be showing zone hints while dragging +}; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 599f96b342..d0db9f6e61 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -1,16 +1,14 @@ #include "pch.h" #include "FancyZones.h" -#include #include #include #include #include -#include #include -#include #include +#include #include #include #include @@ -23,20 +21,13 @@ #include #include #include +#include +#include #include #include +#include +#include #include -#include -#include -#include - -#include "on_thread_executor.h" -#include "trace.h" -#include "VirtualDesktop.h" -#include "MonitorWorkAreaHandler.h" -#include "util.h" - -#include enum class DisplayChangeType { @@ -46,6 +37,22 @@ enum class DisplayChangeType Initialization }; +constexpr wchar_t* DisplayChangeTypeName (const DisplayChangeType type){ + switch (type) + { + case DisplayChangeType::WorkArea: + return L"WorkArea"; + case DisplayChangeType::DisplayChange: + return L"DisplayChange"; + case DisplayChangeType::VirtualDesktop: + return L"VirtualDesktop"; + case DisplayChangeType::Initialization: + return L"Initialization"; + default: + return L""; + } +} + // Non-localizable strings namespace NonLocalizable { @@ -59,7 +66,7 @@ public: FancyZones(HINSTANCE hinstance, std::function disableModuleCallbackFunction) noexcept : SettingsObserver({ SettingId::EditorHotkey, SettingId::PrevTabHotkey, SettingId::NextTabHotkey, SettingId::SpanZonesAcrossMonitors }), m_hinstance(hinstance), - m_windowMoveHandler([this]() { + m_draggingState([this]() { PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL); }) { @@ -82,30 +89,6 @@ public: IFACEMETHODIMP_(void) Destroy() noexcept; - void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen) noexcept - { - if (FancyZonesSettings::settings().spanZonesAcrossMonitors) - { - monitor = NULL; - } - - m_windowMoveHandler.MoveSizeStart(window, monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); - } - - void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) noexcept - { - if (FancyZonesSettings::settings().spanZonesAcrossMonitors) - { - monitor = NULL; - } - m_windowMoveHandler.MoveSizeUpdate(monitor, ptScreen, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); - } - - void MoveSizeEnd(HWND window) noexcept - { - m_windowMoveHandler.MoveSizeEnd(window, m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())); - } - IFACEMETHODIMP_(void) HandleWinHookEvent(const WinHookEvent* data) noexcept { @@ -142,34 +125,35 @@ public: IFACEMETHODIMP_(bool) OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept; + void MoveSizeStart(HWND window, HMONITOR monitor); + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen); + void MoveSizeEnd(); + void WindowCreated(HWND window) noexcept; void ToggleEditor() noexcept; LRESULT WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; void OnDisplayChange(DisplayChangeType changeType) noexcept; - void AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept; + bool AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept; protected: static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; private: - void UpdateWorkAreas() noexcept; + void UpdateWorkAreas(bool updateWindowPositions) noexcept; void CycleWindows(bool reverse) noexcept; bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept; bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept; bool OnSnapHotkey(DWORD vkCode) noexcept; - bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept; + bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept; - void RegisterVirtualDesktopUpdates() noexcept; + void SyncVirtualDesktops() noexcept; void UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept; - std::pair, ZoneIndexSet> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept; - void MoveWindowIntoZone(HWND window, std::shared_ptr workArea, const ZoneIndexSet& zoneIndexSet) noexcept; - bool MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept; - - void OnEditorExitEvent() noexcept; - void UpdateZoneSets() noexcept; + bool MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept; + + void UpdateActiveLayouts() noexcept; bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept; void ApplyQuickLayout(int key) noexcept; void FlashZones() noexcept; @@ -183,8 +167,9 @@ private: const HINSTANCE m_hinstance{}; HWND m_window{}; - WindowMoveHandler m_windowMoveHandler; - MonitorWorkAreaHandler m_workAreaHandler; + std::unique_ptr m_windowDrag{}; + MonitorWorkAreaMap m_workAreaHandler; + DraggingState m_draggingState; wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on @@ -281,7 +266,14 @@ FancyZones::Run() noexcept } }); - PostMessage(m_window, WM_PRIV_VD_INIT, 0, 0); + SyncVirtualDesktops(); + + // id format of applied-layouts and app-zone-history was changed in 0.60 + auto monitors = MonitorUtils::IdentifyMonitors(); + AppliedLayouts::instance().AdjustWorkAreaIds(monitors); + AppZoneHistory::instance().AdjustWorkAreaIds(monitors); + + PostMessage(m_window, WM_PRIV_INIT, 0, 0); } // IFancyZones @@ -308,80 +300,90 @@ FancyZones::VirtualDesktopChanged() noexcept PostMessage(m_window, WM_PRIV_VD_SWITCH, 0, 0); } -std::pair, ZoneIndexSet> FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map>& workAreaMap) noexcept +void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor) { - if (monitor) + m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetAllWorkAreas()); + if (m_windowDrag) { - if (workAreaMap.contains(monitor)) + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { - auto workArea = workAreaMap.at(monitor); - return std::pair, ZoneIndexSet>{ workArea, workArea->GetWindowZoneIndexes(window) }; + monitor = NULL; + } + + m_draggingState.Enable(); + m_draggingState.UpdateDraggingState(); + m_windowDrag->MoveSizeStart(monitor, m_draggingState.IsDragging()); + } +} + +void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) +{ + if (m_windowDrag) + { + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) + { + monitor = NULL; + } + + m_draggingState.UpdateDraggingState(); + m_windowDrag->MoveSizeUpdate(monitor, ptScreen, m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState()); + } +} + +void FancyZones::MoveSizeEnd() +{ + if (m_windowDrag) + { + m_windowDrag->MoveSizeEnd(); + m_draggingState.Disable(); + m_windowDrag = nullptr; + } +} + +bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept +{ + const auto& workAreas = m_workAreaHandler.GetAllWorkAreas(); + WorkArea* workArea{ nullptr }; + ZoneIndexSet indexes{}; + + if (monitor) + { + if (workAreas.contains(monitor)) + { + workArea = workAreas.at(monitor).get(); + if (workArea) + { + indexes = workArea->GetWindowZoneIndexes(window); + } } else { - Logger::debug(L"No work area for the currently active monitor."); + Logger::error(L"Unable to find work area for requested monitor on the active virtual desktop"); } } else { - for (const auto& [mon, workArea] : workAreaMap) + for (const auto& [_, secondaryWorkArea] : workAreas) { - auto zoneIndexSet = workArea->GetWindowZoneIndexes(window); - if (!zoneIndexSet.empty()) + if (secondaryWorkArea) { - return std::pair, ZoneIndexSet>{ workArea, zoneIndexSet }; + indexes = secondaryWorkArea->GetWindowZoneIndexes(window); + workArea = secondaryWorkArea.get(); + if (!indexes.empty()) + { + break; + } } } } - - return std::pair, ZoneIndexSet>{ nullptr, {} }; -} - -void FancyZones::MoveWindowIntoZone(HWND window, std::shared_ptr workArea, const ZoneIndexSet& zoneIndexSet) noexcept -{ - if (workArea) + + if (!indexes.empty() && workArea) { Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); - } - m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, workArea); - AppZoneHistory::instance().UpdateProcessIdToHandleMap(window, workArea->UniqueId()); -} + workArea->MoveWindowIntoZoneByIndexSet(window, indexes); -bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept -{ - auto workAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - if (workAreaMap.empty()) - { - Logger::trace(L"No work area for the current desktop."); - return false; - } - - // Search application history on currently active monitor. - std::pair, ZoneIndexSet> appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, workAreaMap); - - // No application history on currently active monitor - if (appZoneHistoryInfo.second.empty()) - { - // Search application history on primary monitor. - appZoneHistoryInfo = GetAppZoneHistoryInfo(window, primary, workAreaMap); - } - - // No application history on currently active and primary monitors - if (appZoneHistoryInfo.second.empty()) - { - // Search application history on remaining monitors. - appZoneHistoryInfo = GetAppZoneHistoryInfo(window, nullptr, workAreaMap); - } - - if (!appZoneHistoryInfo.second.empty()) - { - MoveWindowIntoZone(window, appZoneHistoryInfo.first, appZoneHistoryInfo.second); return true; } - else - { - Logger::trace(L"App zone history is empty for the processing window on a current virtual desktop"); - } return false; } @@ -423,14 +425,35 @@ void FancyZones::WindowCreated(HWND window) noexcept active = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY); } - bool movedToAppLastZone = false; + bool windowMovedToZone = false; if (moveToAppLastZone) { - movedToAppLastZone = MoveToAppLastZone(window, active, primary); + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) + { + windowMovedToZone = MoveToAppLastZone(window, nullptr); + } + else + { + // Search application history on currently active monitor. + windowMovedToZone = MoveToAppLastZone(window, active); + + if (!windowMovedToZone && primary != active) + { + // Search application history on primary monitor. + windowMovedToZone = MoveToAppLastZone(window, primary); + } + + if (!windowMovedToZone) + { + // Search application history on remaining monitors. + windowMovedToZone = MoveToAppLastZone(window, nullptr); + } + } } + // Open on active monitor if window wasn't zoned - if (openOnActiveMonitor && !movedToAppLastZone) + if (openOnActiveMonitor && !windowMovedToZone) { // window is recreated after switching virtual desktop // avoid moving already opened windows after switching vd @@ -478,7 +501,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept digitPressed = info->vkCode - VK_NUMPAD0; } - bool dragging = m_windowMoveHandler.InDragging(); + bool dragging = m_draggingState.IsDragging(); bool changeLayoutWhileNotDragging = !dragging && !shift && win && ctrl && alt && digitPressed != -1; bool changeLayoutWhileDragging = dragging && digitPressed != -1; @@ -494,7 +517,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept } } - if (m_windowMoveHandler.IsDragEnabled() && shift) + if (m_draggingState.IsDragging() && shift) { return true; } @@ -576,7 +599,6 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa { // Changes in taskbar position resulted in different size of work area. // Invalidate cached work-areas so they can be recreated with latest information. - m_workAreaHandler.Clear(); OnDisplayChange(DisplayChangeType::WorkArea); } } @@ -585,7 +607,6 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa case WM_DISPLAYCHANGE: { // Display resolution changed. Invalidate cached work-areas so they can be recreated with latest information. - m_workAreaHandler.Clear(); OnDisplayChange(DisplayChangeType::DisplayChange); } break; @@ -599,51 +620,38 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa { OnSnapHotkey(static_cast(lparam)); } - else if (message == WM_PRIV_VD_INIT) + else if (message == WM_PRIV_INIT) { + VirtualDesktop::instance().UpdateVirtualDesktopId(); OnDisplayChange(DisplayChangeType::Initialization); } else if (message == WM_PRIV_VD_SWITCH) { + VirtualDesktop::instance().UpdateVirtualDesktopId(); OnDisplayChange(DisplayChangeType::VirtualDesktop); } - else if (message == WM_PRIV_VD_UPDATE) - { - OnDisplayChange(DisplayChangeType::Initialization); - } else if (message == WM_PRIV_EDITOR) { - if (lparam == static_cast(EditorExitKind::Exit)) - { - OnEditorExitEvent(); - } - - { - // Clean up the event either way - m_terminateEditorEvent.release(); - } + // Clean up the event either way + m_terminateEditorEvent.release(); } else if (message == WM_PRIV_MOVESIZESTART) { auto hwnd = reinterpret_cast(wparam); if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) { - MoveSizeStart(hwnd, monitor, ptScreen); + MoveSizeStart(hwnd, monitor); } } else if (message == WM_PRIV_MOVESIZEEND) { - auto hwnd = reinterpret_cast(wparam); - MoveSizeEnd(hwnd); + MoveSizeEnd(); } else if (message == WM_PRIV_LOCATIONCHANGE) { - if (m_windowMoveHandler.InDragging()) + if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) { - if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL)) - { - MoveSizeUpdate(monitor, ptScreen); - } + MoveSizeUpdate(monitor, ptScreen); } } else if (message == WM_PRIV_WINDOWCREATED) @@ -666,7 +674,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE) { AppliedLayouts::instance().LoadData(); - UpdateZoneSets(); + UpdateActiveLayouts(); } else if (message == WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE) { @@ -692,54 +700,55 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept { - Logger::info(L"Display changed, type: {}", changeType); + Logger::info(L"Display changed, type: {}", DisplayChangeTypeName(changeType)); - if (changeType == DisplayChangeType::VirtualDesktop || - changeType == DisplayChangeType::Initialization) + bool updateWindowsPositions = false; + + switch (changeType) { - VirtualDesktop::instance().UpdateVirtualDesktopId(); - - if (changeType == DisplayChangeType::Initialization) - { - RegisterVirtualDesktopUpdates(); - - // id format of applied-layouts and app-zone-history was changed in 0.60 - auto monitors = MonitorUtils::IdentifyMonitors(); - AppliedLayouts::instance().AdjustWorkAreaIds(monitors); - AppZoneHistory::instance().AdjustWorkAreaIds(monitors); - } + case DisplayChangeType::WorkArea: // WorkArea size changed + case DisplayChangeType::DisplayChange: // Resolution changed or display added + updateWindowsPositions = FancyZonesSettings::settings().displayChange_moveWindows; + break; + case DisplayChangeType::VirtualDesktop: // Switched virtual desktop + break; + case DisplayChangeType::Initialization: // Initialization + updateWindowsPositions = FancyZonesSettings::settings().zoneSetChange_moveWindows; + break; + default: + break; } - UpdateWorkAreas(); - - auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - m_windowMoveHandler.AssignWindowsToZones(activeWorkAreas, FancyZonesSettings::settings().displayChange_moveWindows && changeType != DisplayChangeType::VirtualDesktop); + UpdateWorkAreas(updateWindowsPositions); } -void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept +bool FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id) noexcept { - if (m_workAreaHandler.IsNewWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor)) + wil::unique_cotaskmem_string virtualDesktopIdStr; + if (!SUCCEEDED(StringFromCLSID(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), &virtualDesktopIdStr))) { - wil::unique_cotaskmem_string virtualDesktopIdStr; - if (!SUCCEEDED(StringFromCLSID(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), &virtualDesktopIdStr))) - { - Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get()); - } - - FancyZonesDataTypes::WorkAreaId parentId{}; - auto parentArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetPreviousVirtualDesktopId(), monitor); - if (parentArea) - { - parentId = parentArea->UniqueId(); - } - - auto workArea = MakeWorkArea(m_hinstance, monitor, id, parentId); - if (workArea) - { - m_workAreaHandler.AddWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor, workArea); - AppliedLayouts::instance().SaveData(); - } + Logger::debug(L"Add new work area on virtual desktop {}", virtualDesktopIdStr.get()); } + + FancyZonesUtils::Rect rect{}; + if (monitor) + { + rect = MonitorUtils::GetWorkAreaRect(monitor); + } + else + { + rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); + } + + auto workArea = WorkArea::Create(m_hinstance, id, m_workAreaHandler.GetParent(monitor), rect); + if (!workArea) + { + Logger::error(L"Failed to create work area {}", id.toString()); + return false; + } + + m_workAreaHandler.AddWorkArea(monitor, std::move(workArea)); + return true; } LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept @@ -756,8 +765,13 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam, DefWindowProc(window, message, wparam, lparam); } -void FancyZones::UpdateWorkAreas() noexcept +void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept { + Logger::debug(L"Update work areas, update windows positions: {}", updateWindowPositions); + + m_workAreaHandler.SaveParentIds(); + m_workAreaHandler.Clear(); + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) { FancyZonesDataTypes::WorkAreaId workAreaId; @@ -778,6 +792,75 @@ void FancyZones::UpdateWorkAreas() noexcept AddWorkArea(monitor.monitor, workAreaId); } } + + // init previously snapped windows + std::unordered_map windowsToSnap{}; + for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) + { + auto indexes = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + if (indexes.size() == 0) + { + continue; + } + + windowsToSnap.insert({ window, indexes }); + } + + if (FancyZonesSettings::settings().spanZonesAcrossMonitors) // one work area across monitors + { + const auto workArea = m_workAreaHandler.GetWorkArea(nullptr); + if (workArea) + { + for (const auto& [window, zones] : windowsToSnap) + { + workArea->SnapWindow(window, zones, false); + } + } + } + else + { + // first, snap windows to the monitor where they're placed + for (auto iter = windowsToSnap.begin(); iter != windowsToSnap.end();) + { + const auto window = iter->first; + const auto zones = iter->second; + const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + const auto workAreaForMonitor = m_workAreaHandler.GetWorkArea(monitor); + if (workAreaForMonitor && workAreaForMonitor->GetWindowZoneIndexes(window) == zones) + { + workAreaForMonitor->SnapWindow(window, zones, false); + iter = windowsToSnap.erase(iter); + } + else + { + ++iter; + } + } + + // snap rest of the windows to other work areas (in case they were moved after the monitor unplug) + for (const auto& [window, zones] : windowsToSnap) + { + for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) + { + const auto savedIndexes = workArea->GetWindowZoneIndexes(window); + if (savedIndexes == zones) + { + workArea->SnapWindow(window, zones, false); + } + } + } + } + + if (updateWindowPositions) + { + for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) + { + if (workArea) + { + workArea->UpdateWindowPositions(); + } + } + } } void FancyZones::CycleWindows(bool reverse) noexcept @@ -785,7 +868,7 @@ void FancyZones::CycleWindows(bool reverse) noexcept auto window = GetForegroundWindow(); HMONITOR current = WorkAreaKeyFromWindow(window); - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current); + auto workArea = m_workAreaHandler.GetWorkArea(current); if (workArea) { workArea->CycleWindows(window, reverse); @@ -803,13 +886,13 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current); do { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), *currMonitorInfo); - if (m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea)) + auto workArea = m_workAreaHandler.GetWorkArea(*currMonitorInfo); + if (workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */)) { // unassign from previous work area - for (auto& prevWorkArea : m_workAreaHandler.GetAllWorkAreas()) + for (auto& [_, prevWorkArea] : m_workAreaHandler.GetAllWorkAreas()) { - if (workArea != prevWorkArea) + if (prevWorkArea && workArea != prevWorkArea.get()) { prevWorkArea->UnsnapWindow(window); } @@ -839,17 +922,17 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce } else { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current); + auto workArea = m_workAreaHandler.GetWorkArea(current); // Single monitor environment, or combined multi-monitor environment. if (FancyZonesSettings::settings().restoreSize) { - bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea); + bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */); if (!moved) { FancyZonesWindowUtils::RestoreWindowOrigin(window); FancyZonesWindowUtils::RestoreWindowSize(window); } - else + else if (workArea) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); } @@ -857,11 +940,13 @@ bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexce } else { - bool moved = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */, workArea); + bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */); + if (moved) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); } + return moved; } } @@ -879,7 +964,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept { // Multi monitor environment. // First, try to stay on the same monitor - bool success = ProcessDirectedSnapHotkey(window, vkCode, false, m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current)); + bool success = ProcessDirectedSnapHotkey(window, vkCode, false, m_workAreaHandler.GetWorkArea(current)); if (success) { return true; @@ -887,7 +972,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept // If that didn't work, extract zones from all other monitors and target one of them std::vector zoneRects; - std::vector>> zoneRectsInfo; + std::vector> zoneRectsInfo; RECT currentMonitorRect{ .top = 0, .bottom = -1 }; for (const auto& [monitor, monitorRect] : allMonitors) @@ -898,7 +983,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept } else { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor); + auto workArea = m_workAreaHandler.GetWorkArea(monitor); if (workArea) { const auto& layout = workArea->GetLayout(); @@ -935,8 +1020,12 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept { // Moving to another monitor succeeded const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx]; - m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }, workArea); - Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); + if (workArea) + { + workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }); + Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); + } + return true; } @@ -946,7 +1035,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept // Sanity check: the current monitor is valid if (currentMonitorRect.top <= currentMonitorRect.bottom) { - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current); + auto workArea = m_workAreaHandler.GetWorkArea(current); if (workArea) { const auto& layout = workArea->GetLayout(); @@ -980,8 +1069,13 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept { // Moving to another monitor succeeded const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx]; - m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }, workArea); - Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); + + if (workArea) + { + workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx }); + Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); + } + return true; } else @@ -993,7 +1087,7 @@ bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept else { // Single monitor environment, or combined multi-monitor environment. - return ProcessDirectedSnapHotkey(window, vkCode, true, m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), current)); + return ProcessDirectedSnapHotkey(window, vkCode, true, m_workAreaHandler.GetWorkArea(current)); } } @@ -1009,12 +1103,12 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept return (vkCode == VK_LEFT || vkCode == VK_RIGHT) && OnSnapHotkeyBasedOnZoneNumber(window, vkCode); } -bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept +bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept { // Check whether Alt is used in the shortcut key combination if (GetAsyncKeyState(VK_MENU) & 0x8000) { - bool result = m_windowMoveHandler.ExtendWindowByDirectionAndPosition(window, vkCode, workArea); + bool result = workArea && workArea->ExtendWindowByDirectionAndPosition(window, vkCode); if (result) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); @@ -1023,7 +1117,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle } else { - bool result = m_windowMoveHandler.MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle, workArea); + bool result = workArea && workArea->MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle); if (result) { Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); @@ -1032,12 +1126,11 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle } } -void FancyZones::RegisterVirtualDesktopUpdates() noexcept +void FancyZones::SyncVirtualDesktops() noexcept { auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry(); if (guids.has_value()) { - m_workAreaHandler.RegisterUpdates(*guids); AppZoneHistory::instance().RemoveDeletedVirtualDesktops(*guids); AppliedLayouts::instance().RemoveDeletedVirtualDesktops(*guids); } @@ -1092,7 +1185,7 @@ void FancyZones::SettingsUpdate(SettingId id) case SettingId::SpanZonesAcrossMonitors: { m_workAreaHandler.Clear(); - PostMessageW(m_window, WM_PRIV_VD_INIT, NULL, NULL); + PostMessageW(m_window, WM_PRIV_INIT, NULL, NULL); } break; default: @@ -1100,23 +1193,19 @@ void FancyZones::SettingsUpdate(SettingId id) } } -void FancyZones::OnEditorExitEvent() noexcept +void FancyZones::UpdateActiveLayouts() noexcept { - // Collect information about changes in zone layout after editor exited. - UpdateZoneSets(); -} - -void FancyZones::UpdateZoneSets() noexcept -{ - for (auto workArea : m_workAreaHandler.GetAllWorkAreas()) + for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) { - workArea->UpdateActiveZoneSet(); - } + if (workArea) + { + workArea->UpdateActiveZoneSet(); - if (FancyZonesSettings::settings().zoneSetChange_moveWindows) - { - auto activeWorkAreas = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - m_windowMoveHandler.AssignWindowsToZones(activeWorkAreas, true); + if (FancyZonesSettings::settings().zoneSetChange_moveWindows) + { + workArea->UpdateWindowPositions(); + } + } } } @@ -1133,7 +1222,7 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept { HMONITOR monitor = WorkAreaKeyFromWindow(window); - auto workArea = m_workAreaHandler.GetWorkArea(VirtualDesktop::instance().GetCurrentVirtualDesktopId(), monitor); + auto workArea = m_workAreaHandler.GetWorkArea(monitor); if (!workArea) { Logger::error(L"No work area for processing snap hotkey"); @@ -1178,20 +1267,26 @@ void FancyZones::ApplyQuickLayout(int key) noexcept return; } - auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); - AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); - AppliedLayouts::instance().SaveData(); - UpdateZoneSets(); - FlashZones(); + auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(); + if (workArea) + { + AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); + AppliedLayouts::instance().SaveData(); + UpdateActiveLayouts(); + FlashZones(); + } } void FancyZones::FlashZones() noexcept { - if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_windowMoveHandler.IsDragEnabled()) + if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_draggingState.IsDragging()) { - for (auto [monitor, workArea] : m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId())) + for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) { - workArea->FlashZones(); + if (workArea) + { + workArea->FlashZones(); + } } } } @@ -1208,10 +1303,10 @@ std::vector FancyZones::GetMonitorsSorted() noexcept std::vector> FancyZones::GetRawMonitorData() noexcept { std::vector> monitorInfo; - const auto& activeWorkAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(VirtualDesktop::instance().GetCurrentVirtualDesktopId()); + const auto& activeWorkAreaMap = m_workAreaHandler.GetAllWorkAreas(); for (const auto& [monitor, workArea] : activeWorkAreaMap) { - if (workArea->GetLayout() != nullptr) + if (workArea && workArea->GetLayout() != nullptr) { MONITORINFOEX mi; mi.cbSize = sizeof(mi); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp index 4f64465e00..6eaf81d90d 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp @@ -394,7 +394,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId) { - Logger::info(L"Add app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId); + Logger::info(L"Remove app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId); auto processPath = get_process_path_waiting_uwp(window); if (!processPath.empty()) @@ -532,29 +532,6 @@ bool AppZoneHistory::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons return false; } -void AppZoneHistory::UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) -{ - auto processPath = get_process_path_waiting_uwp(window); - if (!processPath.empty()) - { - auto history = m_history.find(processPath); - if (history != std::end(m_history)) - { - auto& perDesktopData = history->second; - for (auto& data : perDesktopData) - { - if (data.workAreaId == workAreaId) - { - DWORD processId = 0; - GetWindowThreadProcessId(window, &processId); - data.processIdToHandleMap[processId] = window; - break; - } - } - } - } -} - ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const { auto processPath = get_process_path_waiting_uwp(window); @@ -564,7 +541,6 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone return {}; } - auto srcVirtualDesktopIDStr = FancyZonesUtils::GuidToString(workAreaId.virtualDesktopId); auto app = processPath; auto pos = processPath.find_last_of('\\'); if (pos != std::string::npos && pos + 1 < processPath.length()) @@ -572,10 +548,7 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone app = processPath.substr(pos + 1); } - if (srcVirtualDesktopIDStr) - { - Logger::debug(L"Get {} zone history on monitor: {}, virtual desktop: {}", app, workAreaId.toString(), srcVirtualDesktopIDStr.value()); - } + Logger::info(L"Get {} zone history on work area: {}", app, workAreaId.toString()); auto history = m_history.find(processPath); if (history == std::end(m_history)) @@ -588,14 +561,9 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone { if (data.zoneSetUuid == zoneSetId && data.workAreaId == workAreaId) { - auto vdStr = FancyZonesUtils::GuidToString(data.workAreaId.virtualDesktopId); - if (vdStr) - { - Logger::debug(L"App zone history found on the device {} with virtual desktop {}", data.workAreaId.toString(), vdStr.value()); - } - if (data.workAreaId.virtualDesktopId == workAreaId.virtualDesktopId || data.workAreaId.virtualDesktopId == GUID_NULL) { + Logger::info(L"App zone history found on the work area {}", data.workAreaId.toString()); return data.zoneIndexSet; } } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h index 1b9db85332..8c69cc5b5a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h @@ -54,7 +54,6 @@ public: std::optional GetZoneHistory(const std::wstring& appPath, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept; bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept; - void UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId); ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const; void SyncVirtualDesktops(); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp index a64e2f4991..1aef2b78e4 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp @@ -37,8 +37,8 @@ namespace JsonUtils for (uint32_t i = 0; i < size; ++i) { json::JsonObject zoneJson = zonesJson.GetObjectAt(i); - const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XID)); - const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YID)); + const int x = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XAxisID)); + const int y = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YAxisID)); const int width = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::WidthID)); const int height = static_cast(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::HeightID)); FancyZonesDataTypes::CanvasLayoutInfo::Rect zone{ x, y, width, height }; @@ -242,7 +242,7 @@ std::optional CustomLayouts::GetLayout(const GUID& id) const noexcep { auto layoutInfo = std::get(customLayout.info); layout.sensitivityRadius = layoutInfo.sensitivityRadius; - layout.zoneCount = (int)layoutInfo.zones.size(); + layout.zoneCount = static_cast(layoutInfo.zones.size()); } return layout; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h index 298edf0dde..efa8d88744 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.h @@ -30,8 +30,8 @@ namespace NonLocalizable const static wchar_t* RefHeightID = L"ref-height"; const static wchar_t* RefWidthID = L"ref-width"; const static wchar_t* ZonesID = L"zones"; - const static wchar_t* XID = L"X"; - const static wchar_t* YID = L"Y"; + const static wchar_t* XAxisID = L"X"; + const static wchar_t* YAxisID = L"Y"; const static wchar_t* WidthID = L"width"; const static wchar_t* HeightID = L"height"; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index c26b30d52e..2fc91a6f5e 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -35,6 +35,7 @@ + @@ -58,7 +59,7 @@ - + @@ -70,17 +71,19 @@ - + + + ../pch.h @@ -110,7 +113,7 @@ - + Create @@ -120,10 +123,11 @@ - + + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index 29ed814973..76bf858a57 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -48,16 +48,13 @@ Header Files - - Header Files - Header Files Header Files - + Header Files @@ -153,9 +150,21 @@ Header Files\FancyZonesData + + Header Files + Header Files + + Header Files + + + Header Files + + + Header Files + @@ -182,16 +191,13 @@ Source Files - - Source Files - Source Files Source Files - + Source Files @@ -251,6 +257,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp index 48698636d5..404486797a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp @@ -7,9 +7,8 @@ UINT WM_PRIV_MOVESIZEEND; UINT WM_PRIV_LOCATIONCHANGE; UINT WM_PRIV_NAMECHANGE; UINT WM_PRIV_WINDOWCREATED; -UINT WM_PRIV_VD_INIT; +UINT WM_PRIV_INIT; UINT WM_PRIV_VD_SWITCH; -UINT WM_PRIV_VD_UPDATE; UINT WM_PRIV_EDITOR; UINT WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE; UINT WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE; @@ -30,9 +29,8 @@ void InitializeWinhookEventIds() WM_PRIV_LOCATIONCHANGE = RegisterWindowMessage(L"{d56c5ee7-58e5-481c-8c4f-8844cf4d0347}"); WM_PRIV_NAMECHANGE = RegisterWindowMessage(L"{b7b30c61-bfa0-4d95-bcde-fc4f2cbf6d76}"); WM_PRIV_WINDOWCREATED = RegisterWindowMessage(L"{bdb10669-75da-480a-9ec4-eeebf09a02d7}"); - WM_PRIV_VD_INIT = RegisterWindowMessage(L"{469818a8-00fa-4069-b867-a1da484fcd9a}"); + WM_PRIV_INIT = RegisterWindowMessage(L"{469818a8-00fa-4069-b867-a1da484fcd9a}"); WM_PRIV_VD_SWITCH = RegisterWindowMessage(L"{128c2cb0-6bdf-493e-abbe-f8705e04aa95}"); - WM_PRIV_VD_UPDATE = RegisterWindowMessage(L"{b8b72b46-f42f-4c26-9e20-29336cf2f22e}"); WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}"); WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE = RegisterWindowMessage(L"{07229b7e-4f22-4357-b136-33c289be2295}"); WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE = RegisterWindowMessage(L"{4686f019-5d3d-4c5c-9051-b7cbbccca77d}"); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h index 25c141c9fa..5558caaf29 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h @@ -5,9 +5,8 @@ extern UINT WM_PRIV_MOVESIZEEND; extern UINT WM_PRIV_LOCATIONCHANGE; extern UINT WM_PRIV_NAMECHANGE; extern UINT WM_PRIV_WINDOWCREATED; -extern UINT WM_PRIV_VD_INIT; // Scheduled when FancyZones is initialized +extern UINT WM_PRIV_INIT; // Scheduled when FancyZones is initialized extern UINT WM_PRIV_VD_SWITCH; // Scheduled when virtual desktop switch occurs -extern UINT WM_PRIV_VD_UPDATE; // Scheduled on virtual desktops update (creation/deletion) extern UINT WM_PRIV_EDITOR; // Scheduled when the editor exits extern UINT WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE; // Scheduled when the watched layout-hotkeys.json file is updated extern UINT WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE; // Scheduled when the watched layout-templates.json file is updated diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp index 2cec6f78f4..dd3298cd8a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProperties.cpp @@ -85,7 +85,7 @@ ZoneIndexSet FancyZonesWindowProperties::RetrieveZoneIndexProperty(HWND window) void FancyZonesWindowProperties::StampMovedOnOpeningProperty(HWND window) { - ::SetPropW(window, ZonedWindowProperties::PropertyMovedOnOpening, (HANDLE)1); + ::SetPropW(window, ZonedWindowProperties::PropertyMovedOnOpening, reinterpret_cast(1)); } bool FancyZonesWindowProperties::RetrieveMovedOnOpeningProperty(HWND window) diff --git a/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h b/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h index 76716ca407..a826e2280f 100644 --- a/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h +++ b/src/modules/fancyzones/FancyZonesLib/GenericKeyHook.h @@ -48,7 +48,7 @@ private: { if (wParam == WM_KEYDOWN || wParam == WM_KEYUP) { - PKBDLLHOOKSTRUCT kbdHookStruct = (PKBDLLHOOKSTRUCT)lParam; + PKBDLLHOOKSTRUCT kbdHookStruct = reinterpret_cast(lParam); if (((kbdHookStruct->vkCode == keys) || ...)) { callback(wParam == WM_KEYDOWN); diff --git a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp new file mode 100644 index 0000000000..22a563aef7 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.cpp @@ -0,0 +1,55 @@ +#include "pch.h" +#include "HighlightedZones.h" + +#include + +HighlightedZones::HighlightedZones() noexcept +{ +} + +const ZoneIndexSet& HighlightedZones::Zones() const noexcept +{ + return m_highlightZone; +} + +bool HighlightedZones::Empty() const noexcept +{ + return m_highlightZone.empty(); +} + +bool HighlightedZones::Update(const Layout* layout, POINT const& point, bool selectManyZones) noexcept +{ + if (!layout) + { + return false; + } + + auto highlightZone = layout->ZonesFromPoint(point); + + if (selectManyZones) + { + if (m_initialHighlightZone.empty()) + { + // first time + m_initialHighlightZone = highlightZone; + } + else + { + highlightZone = layout->GetCombinedZoneRange(m_initialHighlightZone, highlightZone); + } + } + else + { + m_initialHighlightZone = {}; + } + + const bool updated = (highlightZone != m_highlightZone); + m_highlightZone = std::move(highlightZone); + return updated; +} + +void HighlightedZones::Reset() noexcept +{ + m_highlightZone = {}; + m_initialHighlightZone = {}; +} diff --git a/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h new file mode 100644 index 0000000000..b3ba2ca705 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/HighlightedZones.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class Layout; + +class HighlightedZones +{ +public: + HighlightedZones() noexcept; + ~HighlightedZones() = default; + + const ZoneIndexSet& Zones() const noexcept; + bool Empty() const noexcept; + + bool Update(const Layout* layout, POINT const& point, bool selectManyZones) noexcept; + void Reset() noexcept; + +private: + ZoneIndexSet m_initialHighlightZone; + ZoneIndexSet m_highlightZone; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp index 45ab955d76..75cc6b5fa4 100644 --- a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp +++ b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.cpp @@ -69,6 +69,11 @@ void LayoutAssignedWindows::Dismiss(HWND window) FancyZonesWindowProperties::SetTabSortKeyWithinZone(window, std::nullopt); } +std::map LayoutAssignedWindows::SnappedWindows() const noexcept +{ + return m_windowIndexSet; +} + ZoneIndexSet LayoutAssignedWindows::GetZoneIndexSetFromWindow(HWND window) const noexcept { auto it = m_windowIndexSet.find(window); diff --git a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h index ae7c3d4fd2..64377e36b1 100644 --- a/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h +++ b/src/modules/fancyzones/FancyZonesLib/LayoutAssignedWindows.h @@ -19,6 +19,7 @@ public : void Extend(HWND window, const ZoneIndexSet& zones); void Dismiss(HWND window); + std::map SnappedWindows() const noexcept; ZoneIndexSet GetZoneIndexSetFromWindow(HWND window) const noexcept; bool IsZoneEmpty(ZoneIndex zoneIndex) const noexcept; diff --git a/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp b/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp index 1f1402ff15..9585e358c9 100644 --- a/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp +++ b/src/modules/fancyzones/FancyZonesLib/LayoutConfigurator.cpp @@ -197,8 +197,8 @@ ZonesMap LayoutConfigurator::Focus(FancyZonesUtils::Rect workArea, int zoneCount long left{ 100 }; long top{ 100 }; - long right{ left + long(workArea.width() * 0.4) }; - long bottom{ top + long(workArea.height() * 0.4) }; + long right{ left + static_cast(workArea.width() * 0.4) }; + long bottom{ top + static_cast(workArea.height() * 0.4) }; RECT focusZoneRect{ left, top, right, bottom }; diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp index 70209a16c0..6e46babd2a 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.cpp @@ -88,7 +88,7 @@ namespace MonitorUtils // on a particular host computer. IWbemLocator* pLocator = 0; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator); + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(&pLocator)); if (FAILED(hres)) { Logger::error(L"Failed to create IWbemLocator object. {}", get_last_error_or_default(hres)); @@ -207,7 +207,7 @@ namespace MonitorUtils return { .id = str.substr(0, dividerPos), .instanceId = str.substr(dividerPos + 1) }; } - inline bool not_digit(wchar_t ch) + constexpr inline bool not_digit(wchar_t ch) { return '0' <= ch && ch <= '9'; } @@ -383,4 +383,19 @@ namespace MonitorUtils return displays; } + + FancyZonesUtils::Rect GetWorkAreaRect(HMONITOR monitor) + { + if (monitor) + { + MONITORINFO mi{}; + mi.cbSize = sizeof(mi); + if (GetMonitorInfoW(monitor, &mi)) + { + return FancyZonesUtils::Rect(mi.rcWork); + } + } + + return FancyZonesUtils::Rect{}; + } } \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h index a16822b340..dec872f40b 100644 --- a/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h +++ b/src/modules/fancyzones/FancyZonesLib/MonitorUtils.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace MonitorUtils { @@ -19,4 +20,6 @@ namespace MonitorUtils std::vector IdentifyMonitors() noexcept; void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept; + + FancyZonesUtils::Rect GetWorkAreaRect(HMONITOR monitor); }; diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp deleted file mode 100644 index ec97221794..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "pch.h" -#include "MonitorWorkAreaHandler.h" -#include "VirtualDesktop.h" -#include "WorkArea.h" -#include "util.h" - -#include - -std::shared_ptr MonitorWorkAreaHandler::GetWorkArea(const GUID& desktopId, HMONITOR monitor) -{ - auto desktopIt = workAreaMap.find(desktopId); - if (desktopIt != workAreaMap.end()) - { - auto& perDesktopData = desktopIt->second; - auto monitorIt = perDesktopData.find(monitor); - if (monitorIt != std::end(perDesktopData)) - { - return monitorIt->second; - } - } - return nullptr; -} - -std::shared_ptr MonitorWorkAreaHandler::GetWorkAreaFromCursor(const GUID& desktopId) -{ - auto allMonitorsWorkArea = GetWorkArea(desktopId, NULL); - if (allMonitorsWorkArea) - { - // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) - return allMonitorsWorkArea; - } - else - { - // Otherwise, look for the work area based on cursor position - POINT cursorPoint; - if (!GetCursorPos(&cursorPoint)) - { - return nullptr; - } - - return GetWorkArea(desktopId, MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTONULL)); - } -} - -std::shared_ptr MonitorWorkAreaHandler::GetWorkArea(HWND window, const GUID& desktopId) -{ - auto allMonitorsWorkArea = GetWorkArea(desktopId, NULL); - if (allMonitorsWorkArea) - { - // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) - return allMonitorsWorkArea; - } - else - { - // Otherwise, look for the work area based on the window's position - HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - return GetWorkArea(desktopId, monitor); - } -} - -const std::unordered_map>& MonitorWorkAreaHandler::GetWorkAreasByDesktopId(const GUID& desktopId) -{ - if (workAreaMap.contains(desktopId)) - { - return workAreaMap[desktopId]; - } - - static const std::unordered_map> empty{}; - return empty; -} - -std::vector> MonitorWorkAreaHandler::GetAllWorkAreas() -{ - std::vector> workAreas{}; - for (const auto& [desktopId, perDesktopData] : workAreaMap) - { - std::transform(std::begin(perDesktopData), - std::end(perDesktopData), - std::back_inserter(workAreas), - [](const auto& item) { return item.second; }); - } - return workAreas; -} - -void MonitorWorkAreaHandler::AddWorkArea(const GUID& desktopId, HMONITOR monitor, std::shared_ptr& workArea) -{ - if (!workAreaMap.contains(desktopId)) - { - workAreaMap[desktopId] = {}; - - auto desktopIdStr = FancyZonesUtils::GuidToString(desktopId); - if (desktopIdStr) - { - Logger::info(L"Add work area on the desktop {}", desktopIdStr.value()); - } - } - auto& perDesktopData = workAreaMap[desktopId]; - perDesktopData[monitor] = std::move(workArea); -} - -bool MonitorWorkAreaHandler::IsNewWorkArea(const GUID& desktopId, HMONITOR monitor) -{ - if (workAreaMap.contains(desktopId)) - { - const auto& perDesktopData = workAreaMap[desktopId]; - if (perDesktopData.contains(monitor)) - { - return false; - } - } - return true; -} - -void MonitorWorkAreaHandler::RegisterUpdates(const std::vector& active) -{ - std::unordered_set activeVirtualDesktops(std::begin(active), std::end(active)); - for (auto desktopIt = std::begin(workAreaMap); desktopIt != std::end(workAreaMap);) - { - auto activeIt = activeVirtualDesktops.find(desktopIt->first); - if (activeIt == std::end(activeVirtualDesktops)) - { - // virtual desktop deleted, remove entry from the map - desktopIt = workAreaMap.erase(desktopIt); - } - else - { - activeVirtualDesktops.erase(desktopIt->first); // virtual desktop already in map, skip it - ++desktopIt; - } - } - // register new virtual desktops, if any - for (const auto& id : activeVirtualDesktops) - { - workAreaMap[id] = {}; - } -} - -void MonitorWorkAreaHandler::Clear() -{ - workAreaMap.clear(); -} diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h deleted file mode 100644 index 191b82d7b3..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaHandler.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "GuidUtils.h" - -class WorkArea; -struct ZoneColors; -enum struct OverlappingZonesAlgorithm; - -class MonitorWorkAreaHandler -{ -public: - /** - * Get work area based on virtual desktop id and monitor handle. - * - * @param[in] desktopId Virtual desktop identifier. - * @param[in] monitor Monitor handle. - * - * @returns Object representing single work area, interface to all actions available on work area - * (e.g. moving windows through zone layout specified for that work area). - */ - std::shared_ptr GetWorkArea(const GUID& desktopId, HMONITOR monitor); - - /** - * Get work area based on virtual desktop id and the current cursor position. - * - * @param[in] desktopId Virtual desktop identifier. - * - * @returns Object representing single work area, interface to all actions available on work area - * (e.g. moving windows through zone layout specified for that work area). - */ - std::shared_ptr GetWorkAreaFromCursor(const GUID& desktopId); - - /** - * Get work area on which specified window is located. - * - * @param[in] window Window handle. - * @param[in] desktopId GUID current desktop id - * - * @returns Object representing single work area, interface to all actions available on work area - * (e.g. moving windows through zone layout specified for that work area). - */ - std::shared_ptr GetWorkArea(HWND window, const GUID& desktopId); - - /** - * Get map of all work areas on single virtual desktop. Key in the map is monitor handle, while value - * represents single work area. - * - * @param[in] desktopId Virtual desktop identifier. - * - * @returns Map containing pairs of monitor and work area for that monitor (within same virtual desktop). - */ - const std::unordered_map>& GetWorkAreasByDesktopId(const GUID& desktopId); - - /** - * @returns All registered work areas. - */ - std::vector> GetAllWorkAreas(); - - /** - * Register new work area. - * - * @param[in] desktopId Virtual desktop identifier. - * @param[in] monitor Monitor handle. - * @param[in] workAra Object representing single work area. - */ - void AddWorkArea(const GUID& desktopId, HMONITOR monitor, std::shared_ptr& workArea); - - /** - * Check if work area is already registered. - * - * @param[in] desktopId Virtual desktop identifier. - * @param[in] monitor Monitor handle. - * - * @returns Boolean indicating whether work area defined by virtual desktop id and monitor is already registered. - */ - bool IsNewWorkArea(const GUID& desktopId, HMONITOR monitor); - - /** - * Register changes in current virtual desktop layout. - * - * @param[in] active Array of currently active virtual desktop identifiers. - */ - void RegisterUpdates(const std::vector& active); - - /** - * Clear all persisted work area related data. - */ - void Clear(); - -private: - // Work area is uniquely defined by monitor and virtual desktop id. - std::unordered_map>> workAreaMap; -}; diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp new file mode 100644 index 0000000000..486e2739ea --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp @@ -0,0 +1,89 @@ +#include "pch.h" +#include "MonitorWorkAreaMap.h" + +#include + +WorkArea* const MonitorWorkAreaMap::GetWorkArea(HMONITOR monitor) const +{ + auto iter = m_workAreaMap.find(monitor); + if (iter != m_workAreaMap.end()) + { + return iter->second.get(); + } + + return nullptr; +} + +WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromCursor() const +{ + const auto allMonitorsWorkArea = GetWorkArea(nullptr); + if (allMonitorsWorkArea) + { + // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) + return allMonitorsWorkArea; + } + else + { + // Otherwise, look for the work area based on cursor position + POINT cursorPoint; + if (!GetCursorPos(&cursorPoint)) + { + return nullptr; + } + + return GetWorkArea(MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTONULL)); + } +} + +WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromWindow(HWND window) const +{ + const auto allMonitorsWorkArea = GetWorkArea(nullptr); + if (allMonitorsWorkArea) + { + // First, check if there's a work area spanning all monitors (signalled by the NULL monitor handle) + return allMonitorsWorkArea; + } + else + { + // Otherwise, look for the work area based on the window's position + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); + return GetWorkArea(monitor); + } +} + +const std::unordered_map>& MonitorWorkAreaMap::GetAllWorkAreas() const noexcept +{ + return m_workAreaMap; +} + +void MonitorWorkAreaMap::AddWorkArea(HMONITOR monitor, std::unique_ptr workArea) +{ + m_workAreaMap.insert({ monitor, std::move(workArea) }); +} + +FancyZonesDataTypes::WorkAreaId MonitorWorkAreaMap::GetParent(HMONITOR monitor) const +{ + if (m_workAreaParents.contains(monitor)) + { + return m_workAreaParents.at(monitor); + } + + return FancyZonesDataTypes::WorkAreaId{}; +} + +void MonitorWorkAreaMap::SaveParentIds() +{ + m_workAreaParents.clear(); + for (const auto& [monitor, workArea] : m_workAreaMap) + { + if (workArea) + { + m_workAreaParents.insert({ monitor, workArea->UniqueId() }); + } + } +} + +void MonitorWorkAreaMap::Clear() noexcept +{ + m_workAreaMap.clear(); +} diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h new file mode 100644 index 0000000000..45fabda709 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h @@ -0,0 +1,67 @@ +#pragma once + +#include "GuidUtils.h" +#include + +class WorkArea; + +class MonitorWorkAreaMap +{ +public: + /** + * Get work area based on virtual desktop id and monitor handle. + * + * @param[in] monitor Monitor handle. + * + * @returns Object representing single work area, interface to all actions available on work area + * (e.g. moving windows through zone layout specified for that work area). + */ + WorkArea* const GetWorkArea(HMONITOR monitor) const; + + /** + * Get work area based on virtual desktop id and the current cursor position. + * + * @returns Object representing single work area, interface to all actions available on work area + * (e.g. moving windows through zone layout specified for that work area). + */ + WorkArea* const GetWorkAreaFromCursor() const; + + /** + * Get work area on which specified window is located. + * + * @param[in] window Window handle. + * + * @returns Object representing single work area, interface to all actions available on work area + * (e.g. moving windows through zone layout specified for that work area). + */ + WorkArea* const GetWorkAreaFromWindow(HWND window) const; + + /** + * @returns All registered work areas. + */ + const std::unordered_map>& GetAllWorkAreas() const noexcept; + + /** + * Register new work area. + * + * @param[in] monitor Monitor handle. + * @param[in] workAra Object representing single work area. + */ + void AddWorkArea(HMONITOR monitor, std::unique_ptr workArea); + + FancyZonesDataTypes::WorkAreaId GetParent(HMONITOR monitor) const; + + /** + * Saving current work area IDs as parents for later use. + */ + void SaveParentIds(); + + /** + * Clear all persisted work area related data. + */ + void Clear() noexcept; + +private: + std::unordered_map> m_workAreaMap; + std::unordered_map m_workAreaParents{}; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/Settings.cpp b/src/modules/fancyzones/FancyZonesLib/Settings.cpp index 844b4dbc06..4f7e9cf463 100644 --- a/src/modules/fancyzones/FancyZonesLib/Settings.cpp +++ b/src/modules/fancyzones/FancyZonesLib/Settings.cpp @@ -212,7 +212,7 @@ void FancyZonesSettings::LoadSettings() std::wstring apps = std::move(*val); std::vector excludedApps; auto excludedUppercase = apps; - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); @@ -236,7 +236,7 @@ void FancyZonesSettings::LoadSettings() if (auto val = values.get_int_value(NonLocalizable::OverlappingZonesAlgorithmID)) { // Avoid undefined behavior - if (*val >= 0 || *val < (int)OverlappingZonesAlgorithm::EnumElements) + if (*val >= 0 || *val < static_cast(OverlappingZonesAlgorithm::EnumElements)) { auto algorithm = (OverlappingZonesAlgorithm)*val; if (m_settings.overlappingZonesAlgorithm != algorithm) diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp new file mode 100644 index 0000000000..c281f88c51 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.cpp @@ -0,0 +1,243 @@ +#include "pch.h" +#include "WindowDrag.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +WindowDrag::WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas) : + m_window(window), + m_activeWorkAreas(activeWorkAreas), + m_currentWorkArea(nullptr), + m_snappingMode(false) +{ + m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window); + m_windowProperties.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window) && + (!FancyZonesWindowUtils::IsPopupWindow(m_window) || FancyZonesSettings::settings().allowSnapPopupWindows); +} + +WindowDrag::~WindowDrag() +{ + ResetWindowTransparency(); +} + +std::unique_ptr WindowDrag::Create(HWND window, const std::unordered_map>& activeWorkAreas) +{ + if (!FancyZonesWindowProcessing::IsProcessable(window) || + !FancyZonesWindowUtils::IsCandidateForZoning(window) || + FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) + { + return nullptr; + } + + if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) + { + // Notifies user if unable to drag elevated window + FancyZonesNotifications::WarnIfElevationIsRequired(); + return nullptr; + } + + return std::unique_ptr(new WindowDrag(window, activeWorkAreas)); +} + +bool WindowDrag::MoveSizeStart(HMONITOR monitor, bool isSnapping) +{ + auto iter = m_activeWorkAreas.find(monitor); + if (iter == end(m_activeWorkAreas)) + { + return false; + } + + m_currentWorkArea = iter->second.get(); + + SwitchSnappingMode(isSnapping); + + if (m_currentWorkArea) + { + m_currentWorkArea->UnsnapWindow(m_window); + } + + return true; +} + +void WindowDrag::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState) +{ + auto iter = m_activeWorkAreas.find(monitor); + if (isSnapping && iter != m_activeWorkAreas.end()) + { + // The drag has moved to a different monitor. + // Change work area + if (iter->second.get() != m_currentWorkArea) + { + m_highlightedZones.Reset(); + + if (m_currentWorkArea) + { + if (!FancyZonesSettings::settings().showZonesOnAllMonitors) + { + m_currentWorkArea->HideZonesOverlay(); + } + else + { + m_currentWorkArea->ShowZonesOverlay({}, m_window); + } + } + + m_currentWorkArea = iter->second.get(); + } + + if (m_currentWorkArea) + { + POINT ptClient = ptScreen; + MapWindowPoints(nullptr, m_currentWorkArea->GetWorkAreaWindow(), &ptClient, 1); + const bool redraw = m_highlightedZones.Update(m_currentWorkArea->GetLayout().get(), ptClient, isSelectManyZonesState); + if (redraw) + { + m_currentWorkArea->ShowZonesOverlay(m_highlightedZones.Zones(), m_window); + } + } + } + + SwitchSnappingMode(isSnapping); +} + +void WindowDrag::MoveSizeEnd() +{ + if (m_snappingMode) + { + const bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window); + const bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window); + + if ((isStandardWindow == false && hasNoVisibleOwner == true && + m_windowProperties.isStandardWindow == true && m_windowProperties.hasNoVisibleOwner == true) || + FancyZonesWindowUtils::IsWindowMaximized(m_window)) + { + // Abort the zoning, this is a Chromium based tab that is merged back with an existing window + // or if the window is maximized by Windows when the cursor hits the screen top border + } + else if (m_currentWorkArea) + { + m_currentWorkArea->MoveWindowIntoZoneByIndexSet(m_window, m_highlightedZones.Zones()); + } + } + else + { + FancyZonesWindowUtils::ResetRoundCornersPreference(m_window); + if (FancyZonesSettings::settings().restoreSize) + { + if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) + { + ::RemoveProp(m_window, ZonedWindowProperties::PropertyRestoreSizeID); + } + else if (!FancyZonesWindowUtils::IsWindowMaximized(m_window)) + { + FancyZonesWindowUtils::RestoreWindowSize(m_window); + } + } + } + + SwitchSnappingMode(false); +} + +void WindowDrag::SwitchSnappingMode(bool isSnapping) +{ + if (!m_snappingMode && isSnapping) // turn on + { + m_highlightedZones.Reset(); + SetWindowTransparency(); + + if (FancyZonesSettings::settings().showZonesOnAllMonitors) + { + for (const auto& [_, workArea] : m_activeWorkAreas) + { + if (workArea) + { + workArea->ShowZonesOverlay({}, m_window); + } + } + } + else if (m_currentWorkArea) + { + m_currentWorkArea->ShowZonesOverlay({}, m_window); + } + + if (m_currentWorkArea) + { + Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get()); + } + } + else if (m_snappingMode && !isSnapping) // turn off + { + ResetWindowTransparency(); + m_highlightedZones.Reset(); + + // Hide all layouts (regardless of settings) + for (auto& [_, workArea] : m_activeWorkAreas) + { + if (workArea) + { + workArea->HideZonesOverlay(); + } + } + + if (m_currentWorkArea) + { + Trace::WorkArea::MoveOrResizeEnd(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get()); + } + } + + m_snappingMode = isSnapping; +} + +void WindowDrag::SetWindowTransparency() +{ + if (FancyZonesSettings::settings().makeDraggedWindowTransparent) + { + m_windowProperties.exstyle = GetWindowLong(m_window, GWL_EXSTYLE); + + SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle | WS_EX_LAYERED); + + if (!GetLayeredWindowAttributes(m_window, &m_windowProperties.crKey, &m_windowProperties.alpha, &m_windowProperties.dwFlags)) + { + Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + return; + } + + if (!SetLayeredWindowAttributes(m_window, 0, (255 * 50) / 100, LWA_ALPHA)) + { + Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + return; + } + + m_windowProperties.transparencySet = true; + } +} + +void WindowDrag::ResetWindowTransparency() +{ + if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowProperties.transparencySet) + { + bool reset = true; + if (!SetLayeredWindowAttributes(m_window, m_windowProperties.crKey, m_windowProperties.alpha, m_windowProperties.dwFlags)) + { + Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); + reset = false; + } + + if (SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle) == 0) + { + Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError())); + reset = false; + } + + m_windowProperties.transparencySet = !reset; + } +} diff --git a/src/modules/fancyzones/FancyZonesLib/WindowDrag.h b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h new file mode 100644 index 0000000000..0956af9841 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/WindowDrag.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +class WorkArea; + +class WindowDrag +{ + WindowDrag(HWND window, const std::unordered_map>& activeWorkAreas); + +public: + static std::unique_ptr Create(HWND window, const std::unordered_map>& activeWorkAreas); + ~WindowDrag(); + + bool MoveSizeStart(HMONITOR monitor, bool isSnapping); + void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState); + void MoveSizeEnd(); + +private: + void SwitchSnappingMode(bool isSnapping); + + void SetWindowTransparency(); + void ResetWindowTransparency(); + + struct WindowProperties + { + // True if from the styles the window looks like a standard window + bool isStandardWindow = false; + // True if the window is a top-level window that does not have a visible owner + bool hasNoVisibleOwner = false; + // Properties to restore after dragging + long exstyle = 0; + COLORREF crKey = RGB(0, 0, 0); + DWORD dwFlags = 0; + BYTE alpha = 0; + bool transparencySet{false}; + }; + + const HWND m_window; + WindowProperties m_windowProperties; // MoveSizeWindowInfo of the window at the moment when dragging started + + const std::unordered_map>& m_activeWorkAreas; // all WorkAreas on current virtual desktop, mapped with monitors + WorkArea* m_currentWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. + + bool m_snappingMode{ false }; + + HighlightedZones m_highlightedZones; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp deleted file mode 100644 index 1e2867513e..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.cpp +++ /dev/null @@ -1,372 +0,0 @@ -#include "pch.h" -#include "WindowMoveHandler.h" - -#include -#include -#include -#include - -#include "FancyZonesData/AppZoneHistory.h" -#include "Settings.h" -#include "WorkArea.h" -#include -#include -#include - -WindowMoveHandler::WindowMoveHandler(const std::function& keyUpdateCallback) : - m_mouseState(false), - m_mouseHook(std::bind(&WindowMoveHandler::OnMouseDown, this)), - m_leftShiftKeyState(keyUpdateCallback), - m_rightShiftKeyState(keyUpdateCallback), - m_ctrlKeyState(keyUpdateCallback), - m_keyUpdateCallback(keyUpdateCallback) -{ -} - -void WindowMoveHandler::MoveSizeStart(HWND window, HMONITOR monitor, POINT const& /*ptScreen*/, const std::unordered_map>& workAreaMap) noexcept -{ - if (!FancyZonesWindowProcessing::IsProcessable(window)) - { - return; - } - - if (!FancyZonesWindowUtils::IsCandidateForZoning(window) || FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) - { - return; - } - - m_draggedWindowInfo.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(window); - m_draggedWindowInfo.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(window) && (!FancyZonesWindowUtils::IsPopupWindow(window) || FancyZonesSettings::settings().allowSnapPopupWindows); - m_inDragging = true; - - auto iter = workAreaMap.find(monitor); - if (iter == end(workAreaMap)) - { - return; - } - - m_draggedWindow = window; - - if (FancyZonesSettings::settings().mouseSwitch) - { - m_mouseHook.enable(); - } - - m_leftShiftKeyState.enable(); - m_rightShiftKeyState.enable(); - m_ctrlKeyState.enable(); - - // This updates m_dragEnabled depending on if the shift key is being held down - UpdateDragState(); - - if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) - { - // Notifies user if unable to drag elevated window - FancyZonesNotifications::WarnIfElevationIsRequired(); - m_dragEnabled = false; - } - - if (m_dragEnabled) - { - m_draggedWindowWorkArea = iter->second; - SetWindowTransparency(m_draggedWindow); - m_draggedWindowWorkArea->MoveSizeEnter(m_draggedWindow); - if (FancyZonesSettings::settings().showZonesOnAllMonitors) - { - for (const auto& [keyMonitor, workArea] : workAreaMap) - { - // Skip calling ShowZonesOverlay for iter->second (m_draggedWindowWorkArea) since it - // was already called in MoveSizeEnter - const bool moveSizeEnterCalled = workArea == m_draggedWindowWorkArea; - if (workArea && !moveSizeEnterCalled) - { - workArea->ShowZonesOverlay(); - } - } - } - } - else if (m_draggedWindowWorkArea) - { - ResetWindowTransparency(); - m_draggedWindowWorkArea = nullptr; - for (const auto& [keyMonitor, workArea] : workAreaMap) - { - if (workArea) - { - workArea->HideZonesOverlay(); - } - } - } - - auto workArea = workAreaMap.find(monitor); - if (workArea != workAreaMap.end()) - { - workArea->second->UnsnapWindow(window); - } -} - -void WindowMoveHandler::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept -{ - if (!m_inDragging) - { - return; - } - - // This updates m_dragEnabled depending on if the shift key is being held down. - UpdateDragState(); - - if (m_draggedWindowWorkArea) - { - // Update the WorkArea already handling move/size - if (!m_dragEnabled) - { - // Drag got disabled, tell it to cancel and hide all windows - m_draggedWindowWorkArea = nullptr; - ResetWindowTransparency(); - - for (auto [keyMonitor, workArea] : workAreaMap) - { - if (workArea) - { - workArea->HideZonesOverlay(); - } - } - } - else - { - auto iter = workAreaMap.find(monitor); - if (iter != workAreaMap.end()) - { - if (iter->second != m_draggedWindowWorkArea) - { - // The drag has moved to a different monitor. - m_draggedWindowWorkArea->ClearSelectedZones(); - if (!FancyZonesSettings::settings().showZonesOnAllMonitors) - { - m_draggedWindowWorkArea->HideZonesOverlay(); - } - - m_draggedWindowWorkArea = iter->second; - m_draggedWindowWorkArea->MoveSizeEnter(m_draggedWindow); - } - - for (auto [keyMonitor, workArea] : workAreaMap) - { - workArea->MoveSizeUpdate(ptScreen, m_dragEnabled, m_ctrlKeyState.state()); - } - } - } - } - else if (m_dragEnabled) - { - // We'll get here if the user presses/releases shift while dragging. - // Restart the drag on the WorkArea that m_draggedWindow is on - MoveSizeStart(m_draggedWindow, monitor, ptScreen, workAreaMap); - - // m_dragEnabled could get set to false if we're moving an elevated window. - // In that case do not proceed. - if (m_dragEnabled) - { - MoveSizeUpdate(monitor, ptScreen, workAreaMap); - } - } -} - -void WindowMoveHandler::MoveSizeEnd(HWND window, const std::unordered_map>& workAreaMap) noexcept -{ - if (window != m_draggedWindow) - { - return; - } - - bool leftShiftPressed = m_leftShiftKeyState.state(); - bool rightShiftPressed = m_rightShiftKeyState.state(); - - m_mouseHook.disable(); - m_leftShiftKeyState.disable(); - m_rightShiftKeyState.disable(); - m_ctrlKeyState.disable(); - - if (m_draggedWindowWorkArea) - { - auto workArea = std::move(m_draggedWindowWorkArea); - ResetWindowTransparency(); - - bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(window); - bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(window); - - if ((isStandardWindow == false && hasNoVisibleOwner == true && - m_draggedWindowInfo.isStandardWindow == true && m_draggedWindowInfo.hasNoVisibleOwner == true) || - FancyZonesWindowUtils::IsWindowMaximized(window)) - { - // Abort the zoning, this is a Chromium based tab that is merged back with an existing window - // or if the window is maximized by Windows when the cursor hits the screen top border - } - else - { - if (FancyZonesSettings::settings().shiftDrag) - { - if (leftShiftPressed) - { - FancyZonesUtils::SwallowKey(VK_LSHIFT); - } - - if (rightShiftPressed) - { - FancyZonesUtils::SwallowKey(VK_RSHIFT); - } - } - - workArea->MoveSizeEnd(m_draggedWindow); - } - } - else - { - if (FancyZonesSettings::settings().restoreSize) - { - if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent()) - { - ::RemoveProp(window, ZonedWindowProperties::PropertyRestoreSizeID); - } - else if (!FancyZonesWindowUtils::IsWindowMaximized(window)) - { - FancyZonesWindowUtils::RestoreWindowSize(window); - } - } - - FancyZonesWindowUtils::ResetRoundCornersPreference(window); - - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (monitor) - { - auto workArea = workAreaMap.find(monitor); - if (workArea != workAreaMap.end()) - { - const auto workAreaPtr = workArea->second; - const auto& layout = workAreaPtr->GetLayout(); - if (layout) - { - auto guidStr = FancyZonesUtils::GuidToString(layout->Id()); - if (guidStr.has_value()) - { - AppZoneHistory::instance().RemoveAppLastZone(window, workAreaPtr->UniqueId(), guidStr.value()); - } - } - - workAreaPtr->UnsnapWindow(window); - } - } - - FancyZonesWindowProperties::RemoveZoneIndexProperty(window); - } - - m_inDragging = false; - m_dragEnabled = false; - m_mouseState = false; - m_draggedWindow = nullptr; - - // Also, hide all windows (regardless of settings) - for (auto [keyMonitor, workArea] : workAreaMap) - { - if (workArea) - { - workArea->HideZonesOverlay(); - } - } -} - -void WindowMoveHandler::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, std::shared_ptr workArea) noexcept -{ - if (window != m_draggedWindow) - { - workArea->MoveWindowIntoZoneByIndexSet(window, indexSet); - } -} - -bool WindowMoveHandler::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept -{ - return workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, cycle); -} - -bool WindowMoveHandler::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept -{ - return workArea && workArea->MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle); -} - -bool WindowMoveHandler::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode, std::shared_ptr workArea) noexcept -{ - return workArea && workArea->ExtendWindowByDirectionAndPosition(window, vkCode); -} - -void WindowMoveHandler::AssignWindowsToZones(const std::unordered_map>& activeWorkAreas, bool updatePositions) noexcept -{ - for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop()) - { - auto zoneIndexSet = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); - if (zoneIndexSet.size() == 0) - { - continue; - } - - auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); - if (monitor && activeWorkAreas.contains(monitor)) - { - activeWorkAreas.at(monitor)->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, updatePositions); - } - } -} - -void WindowMoveHandler::UpdateDragState() noexcept -{ - if (FancyZonesSettings::settings().shiftDrag) - { - m_dragEnabled = ((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_mouseState); - } - else - { - m_dragEnabled = !((m_leftShiftKeyState.state() || m_rightShiftKeyState.state()) ^ m_mouseState); - } -} - -void WindowMoveHandler::SetWindowTransparency(HWND window) noexcept -{ - if (FancyZonesSettings::settings().makeDraggedWindowTransparent) - { - m_windowTransparencyProperties.draggedWindowExstyle = GetWindowLong(window, GWL_EXSTYLE); - - SetWindowLong(window, - GWL_EXSTYLE, - m_windowTransparencyProperties.draggedWindowExstyle | WS_EX_LAYERED); - - if (!GetLayeredWindowAttributes(window, &m_windowTransparencyProperties.draggedWindowCrKey, &m_windowTransparencyProperties.draggedWindowInitialAlpha, &m_windowTransparencyProperties.draggedWindowDwFlags)) - { - Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); - return; - } - - m_windowTransparencyProperties.draggedWindow = window; - - if (!SetLayeredWindowAttributes(window, 0, (255 * 50) / 100, LWA_ALPHA)) - { - Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError())); - } - } -} - -void WindowMoveHandler::ResetWindowTransparency() noexcept -{ - if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowTransparencyProperties.draggedWindow != nullptr) - { - if (!SetLayeredWindowAttributes(m_windowTransparencyProperties.draggedWindow, m_windowTransparencyProperties.draggedWindowCrKey, m_windowTransparencyProperties.draggedWindowInitialAlpha, m_windowTransparencyProperties.draggedWindowDwFlags)) - { - Logger::error(L"Window transparency: SetLayeredWindowAttributes failed"); - } - - if (SetWindowLong(m_windowTransparencyProperties.draggedWindow, GWL_EXSTYLE, m_windowTransparencyProperties.draggedWindowExstyle) == 0) - { - Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError())); - } - - m_windowTransparencyProperties.draggedWindow = nullptr; - } -} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h b/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h deleted file mode 100644 index 67d96036f0..0000000000 --- a/src/modules/fancyzones/FancyZonesLib/WindowMoveHandler.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "FancyZonesWindowProperties.h" -#include "KeyState.h" -#include "SecondaryMouseButtonsHook.h" - -#include - -interface IFancyZonesSettings; -class WorkArea; - -class WindowMoveHandler -{ -public: - WindowMoveHandler(const std::function& keyUpdateCallback); - - void MoveSizeStart(HWND window, HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept; - void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, const std::unordered_map>& workAreaMap) noexcept; - void MoveSizeEnd(HWND window, const std::unordered_map>& workAreaMap) noexcept; - - void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, std::shared_ptr workArea) noexcept; - bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept; - bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle, std::shared_ptr workArea) noexcept; - bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode, std::shared_ptr workArea) noexcept; - - void AssignWindowsToZones(const std::unordered_map>& activeWorkAreas, bool updatePositions) noexcept; - - inline void OnMouseDown() noexcept - { - m_mouseState = !m_mouseState; - m_keyUpdateCallback(); - } - - inline bool IsDragEnabled() const noexcept - { - return m_dragEnabled; - } - - inline bool InDragging() const noexcept - { - return m_inDragging; - } - -private: - struct WindowTransparencyProperties - { - HWND draggedWindow = nullptr; - long draggedWindowExstyle = 0; - COLORREF draggedWindowCrKey = RGB(0, 0, 0); - DWORD draggedWindowDwFlags = 0; - BYTE draggedWindowInitialAlpha = 0; - }; - - // MoveSize related window properties - struct MoveSizeWindowInfo - { - // True if from the styles the window looks like a standard window - bool isStandardWindow = false; - // True if the window is a top-level window that does not have a visible owner - bool hasNoVisibleOwner = false; - }; - - void UpdateDragState() noexcept; - - void SetWindowTransparency(HWND window) noexcept; - void ResetWindowTransparency() noexcept; - - bool m_inDragging{}; // Whether or not a move/size operation is currently active - HWND m_draggedWindow{}; // The window that is being moved/sized - MoveSizeWindowInfo m_draggedWindowInfo; // MoveSizeWindowInfo of the window at the moment when dragging started - std::shared_ptr m_draggedWindowWorkArea; // "Active" WorkArea, where the move/size is happening. Will update as drag moves between monitors. - bool m_dragEnabled{}; // True if we should be showing zone hints while dragging - - WindowTransparencyProperties m_windowTransparencyProperties; - - std::atomic m_mouseState; - SecondaryMouseButtonsHook m_mouseHook; - KeyState m_leftShiftKeyState; - KeyState m_rightShiftKeyState; - KeyState m_ctrlKeyState; - std::function m_keyUpdateCallback; -}; diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp index 41f079f6b6..c9252ba5df 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -226,7 +226,7 @@ bool FancyZonesWindowUtils::IsCandidateForZoning(HWND window) } std::wstring processPath = get_process_path_waiting_uwp(window); - CharUpperBuffW(const_cast(processPath).data(), (DWORD)processPath.length()); + CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length())); if (IsExcludedByUser(processPath)) { return false; diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 6b83fee0e5..afed03881f 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -109,8 +109,9 @@ namespace WindowPool windowPool; } -WorkArea::WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId) : - m_uniqueId(uniqueId) +WorkArea::WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesUtils::Rect& workAreaRect) : + m_uniqueId(uniqueId), + m_workAreaRect(workAreaRect) { WNDCLASSEXW wcex{}; wcex.cbSize = sizeof(WNDCLASSEX); @@ -126,87 +127,12 @@ WorkArea::~WorkArea() windowPool.FreeZonesOverlayWindow(m_window); } -HRESULT WorkArea::MoveSizeEnter(HWND window) noexcept -{ - m_windowMoveSize = window; - m_highlightZone = {}; - m_initialHighlightZone = {}; - ShowZonesOverlay(); - Trace::WorkArea::MoveOrResizeStarted(m_layout.get(), m_layoutWindows.get()); - return S_OK; -} - -HRESULT WorkArea::MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept -{ - if (!m_layout) - { - return -1; - } - - bool redraw = false; - POINT ptClient = ptScreen; - MapWindowPoints(nullptr, m_window, &ptClient, 1); - - if (dragEnabled) - { - auto highlightZone = ZonesFromPoint(ptClient); - - if (selectManyZones) - { - if (m_initialHighlightZone.empty()) - { - // first time - m_initialHighlightZone = highlightZone; - } - else - { - highlightZone = m_layout->GetCombinedZoneRange(m_initialHighlightZone, highlightZone); - } - } - else - { - m_initialHighlightZone = {}; - } - - redraw = (highlightZone != m_highlightZone); - m_highlightZone = std::move(highlightZone); - } - else if (m_highlightZone.size()) - { - m_highlightZone = {}; - redraw = true; - } - - if (redraw && m_zonesOverlay) - { - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); - } - - return S_OK; -} - -HRESULT WorkArea::MoveSizeEnd(HWND window) noexcept -{ - if (m_windowMoveSize != window) - { - return E_INVALIDARG; - } - - MoveWindowIntoZoneByIndexSet(window, m_highlightZone); - - Trace::WorkArea::MoveOrResizeEnd(m_layout.get(), m_layoutWindows.get()); - - HideZonesOverlay(); - m_windowMoveSize = nullptr; - return S_OK; -} - -void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept +void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) { MoveWindowIntoZoneByIndexSet(window, { index }); } -void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/) noexcept +void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty()) { @@ -217,18 +143,18 @@ void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& ind if (updatePosition) { - auto rect = m_layout->GetCombinedZonesRect(indexSet); - auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); - FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); + const auto rect = m_layout->GetCombinedZonesRect(indexSet); + if (rect.bottom - rect.top > 0 && rect.right - rect.left > 0) + { + const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); + FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); + } } - m_layoutWindows->Assign(window, indexSet); - FancyZonesWindowProperties::StampZoneIndexProperty(window, indexSet); - - SaveWindowProcessToZoneIndex(window); + SnapWindow(window, indexSet); } -bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept +bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) { @@ -236,7 +162,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, } auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window); - auto numZones = m_layout->Zones().size(); + const auto numZones = m_layout->Zones().size(); // The window was not assigned to any zone here if (zoneIndexes.size() == 0) @@ -245,7 +171,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, } else { - ZoneIndex oldId = zoneIndexes[0]; + const ZoneIndex oldId = zoneIndexes[0]; // We reached the edge if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast(numZones) - 1)) @@ -271,15 +197,10 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, } } - if (!FancyZonesWindowUtils::HasVisibleOwner(window)) - { - SaveWindowProcessToZoneIndex(window); - } - return true; } -bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept +bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) { @@ -290,7 +211,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod std::vector usedZoneIndices(zones.size(), false); auto windowZones = m_layoutWindows->GetZoneIndexSetFromWindow(window); - for (ZoneIndex id : windowZones) + for (const ZoneIndex id : windowZones) { usedZoneIndices[id] = true; } @@ -324,7 +245,6 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod if (result < zoneRects.size()) { MoveWindowIntoZoneByIndex(window, freeZoneIndices[result]); - SaveWindowProcessToZoneIndex(window); Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get()); return true; } @@ -340,7 +260,6 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod if (result < zoneRects.size()) { MoveWindowIntoZoneByIndex(window, result); - SaveWindowProcessToZoneIndex(window); Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get()); return true; } @@ -349,7 +268,7 @@ bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCod return false; } -bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept +bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) { if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) { @@ -381,7 +300,7 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe } else { - for (ZoneIndex idx : appliedZones) + for (const ZoneIndex idx : appliedZones) { usedZoneIndices[idx] = true; } @@ -401,7 +320,7 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe } } - auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects); + const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects); if (result < zoneRects.size()) { ZoneIndex targetZone = freeZoneIndices[result]; @@ -431,14 +350,11 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe resultIndexSet = m_layout->GetCombinedZoneRange(extendModeData->windowInitialIndexSet[window], { targetZone }); } - auto rect = m_layout->GetCombinedZonesRect(resultIndexSet); - auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); + const auto rect = m_layout->GetCombinedZonesRect(resultIndexSet); + const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); - m_layoutWindows->Extend(window, resultIndexSet); - FancyZonesWindowProperties::StampZoneIndexProperty(window, resultIndexSet); - - SaveWindowProcessToZoneIndex(window); + SnapWindow(window, resultIndexSet, true); return true; } @@ -446,39 +362,60 @@ bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noe return false; } -void WorkArea::SaveWindowProcessToZoneIndex(HWND window) noexcept +void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend) { - if (m_layout && m_layoutWindows) + if (!m_layoutWindows || !m_layout) { - auto zoneIndexSet = m_layoutWindows->GetZoneIndexSetFromWindow(window); - if (zoneIndexSet.size()) - { - auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); - if (guidStr.has_value()) - { - AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zoneIndexSet); - } - } + return; } -} -bool WorkArea::UnsnapWindow(HWND window) noexcept -{ - if (!m_layoutWindows) + if (extend) { - return false; + m_layoutWindows->Extend(window, zones); + } + else + { + m_layoutWindows->Assign(window, zones); } - if (!m_layoutWindows->GetZoneIndexSetFromWindow(window).empty()) + auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); + if (guidStr.has_value()) { - m_layoutWindows->Dismiss(window); - return true; + AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zones); } - return false; + FancyZonesWindowProperties::StampZoneIndexProperty(window, zones); } -ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const noexcept +void WorkArea::UnsnapWindow(HWND window) +{ + if (!m_layoutWindows || !m_layout) + { + return; + } + + m_layoutWindows->Dismiss(window); + + auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id()); + if (guidStr.has_value()) + { + AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, guidStr.value()); + } + + FancyZonesWindowProperties::RemoveZoneIndexProperty(window); +} + +const GUID WorkArea::GetLayoutId() const noexcept +{ + if (m_layout) + { + return m_layout->Id(); + } + + return GUID{}; +} + +ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const { if (m_layout) { @@ -500,30 +437,37 @@ ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const noexcept return {}; } -void WorkArea::ShowZonesOverlay() noexcept +void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/) { - if (m_window && m_layout) + if (m_layout && m_zonesOverlay) { - SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + SetWorkAreaWindowAsTopmost(draggedWindow); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), highlight, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); m_zonesOverlay->Show(); } } -void WorkArea::HideZonesOverlay() noexcept +void WorkArea::HideZonesOverlay() { - if (m_window) + if (m_zonesOverlay) { m_zonesOverlay->Hide(); - m_keyLast = 0; - m_windowMoveSize = nullptr; - m_highlightZone = {}; } } -void WorkArea::UpdateActiveZoneSet() noexcept +void WorkArea::FlashZones() { - bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); + if (m_layout && m_zonesOverlay) + { + SetWorkAreaWindowAsTopmost(nullptr); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_zonesOverlay->Flash(); + } +} + +void WorkArea::UpdateActiveZoneSet() +{ + const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); if (!isLayoutAlreadyApplied) { AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId); @@ -532,12 +476,25 @@ void WorkArea::UpdateActiveZoneSet() noexcept CalculateZoneSet(); if (m_window && m_layout) { - m_highlightZone.clear(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); + m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); } } -void WorkArea::CycleWindows(HWND window, bool reverse) noexcept +void WorkArea::UpdateWindowPositions() +{ + if (!m_layoutWindows) + { + return; + } + + const auto& snappedWindows = m_layoutWindows->SnappedWindows(); + for (const auto& [window, zones] : snappedWindows) + { + MoveWindowIntoZoneByIndexSet(window, zones, true); + } +} + +void WorkArea::CycleWindows(HWND window, bool reverse) { if (m_layoutWindows) { @@ -545,28 +502,9 @@ void WorkArea::CycleWindows(HWND window, bool reverse) noexcept } } -void WorkArea::ClearSelectedZones() noexcept -{ - if (m_highlightZone.size() && m_layout) - { - m_highlightZone.clear(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), m_highlightZone, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); - } -} - -void WorkArea::FlashZones() noexcept -{ - if (m_window && m_layout) - { - SetAsTopmostWindow(); - m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); - m_zonesOverlay->Flash(); - } -} - #pragma region private -bool WorkArea::InitWindow(HINSTANCE hinstance) noexcept +bool WorkArea::InitWindow(HINSTANCE hinstance) { m_window = windowPool.NewZonesOverlayWindow(m_workAreaRect, hinstance, this); if (!m_window) @@ -579,11 +517,11 @@ bool WorkArea::InitWindow(HINSTANCE hinstance) noexcept return true; } -void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept +void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) { - Logger::info(L"Initialize layout on {}", m_uniqueId.toString()); + Logger::info(L"Initialize layout on {}, work area rect = {}x{}", m_uniqueId.toString(), m_workAreaRect.width(), m_workAreaRect.height()); - bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); + const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); if (!isLayoutAlreadyApplied) { if (parentUniqueId.virtualDesktopId != GUID_NULL) @@ -599,7 +537,7 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) CalculateZoneSet(); } -void WorkArea::CalculateZoneSet() noexcept +void WorkArea::CalculateZoneSet() { const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId); if (!appliedLayout.has_value()) @@ -609,7 +547,7 @@ void WorkArea::CalculateZoneSet() noexcept } m_layout = std::make_unique(appliedLayout.value()); - m_layout->Init(m_workAreaRect, m_monitor); + m_layout->Init(m_workAreaRect, m_uniqueId.monitorId.monitor); if (!m_layoutWindows) { @@ -639,39 +577,19 @@ LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept return 0; } -ZoneIndexSet WorkArea::ZonesFromPoint(POINT pt) noexcept -{ - if (m_layout) - { - return m_layout->ZonesFromPoint(pt); - } - - return {}; -} - -void WorkArea::SetAsTopmostWindow() noexcept +void WorkArea::SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept { if (!m_window) { return; } - UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE; - - HWND windowInsertAfter = m_windowMoveSize; - if (windowInsertAfter == nullptr) - { - windowInsertAfter = HWND_TOPMOST; - } + HWND windowInsertAfter = draggedWindow ? draggedWindow : HWND_TOPMOST; + constexpr UINT flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE; SetWindowPos(m_window, windowInsertAfter, 0, 0, 0, 0, flags); } -void WorkArea::LogInitializationError() -{ - Logger::error(L"Unable to get monitor info, {}", get_last_error_or_default(GetLastError())); -} - #pragma endregion LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept @@ -686,4 +604,4 @@ LRESULT CALLBACK WorkArea::s_WndProc(HWND window, UINT message, WPARAM wparam, L return (thisRef != nullptr) ? thisRef->WndProc(message, wparam, lparam) : DefWindowProc(window, message, wparam, lparam); -} +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.h b/src/modules/fancyzones/FancyZonesLib/WorkArea.h index 61458f7179..5e04c87aee 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.h +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.h @@ -3,17 +3,27 @@ #include #include #include -#include class ZonesOverlay; class WorkArea { -public: - WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId); - ~WorkArea(); + WorkArea(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesUtils::Rect& workAreaRect); public: + ~WorkArea(); + + static std::unique_ptr Create(HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId, const FancyZonesUtils::Rect& workAreaRect) + { + auto self = std::unique_ptr(new WorkArea(hinstance, uniqueId, workAreaRect)); + if (!self->Init(hinstance, parentUniqueId)) + { + return nullptr; + } + + return self; + } + inline bool Init([[maybe_unused]] HINSTANCE hinstance, const FancyZonesDataTypes::WorkAreaId& parentUniqueId) { #ifndef UNIT_TESTS @@ -23,103 +33,52 @@ public: } #endif InitLayout(parentUniqueId); + return true; } - - inline bool InitWorkAreaRect(HMONITOR monitor) - { - m_monitor = monitor; - -#if defined(UNIT_TESTS) - m_workAreaRect = FancyZonesUtils::Rect({ 0, 0, 1920, 1080 }); -#else - - if (monitor) - { - MONITORINFO mi{}; - mi.cbSize = sizeof(mi); - if (!GetMonitorInfoW(monitor, &mi)) - { - return false; - } - - m_workAreaRect = FancyZonesUtils::Rect(mi.rcWork); - } - else - { - m_workAreaRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>(); - } -#endif - - return true; - } - + FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; } const std::unique_ptr& GetLayout() const noexcept { return m_layout; } const std::unique_ptr& GetLayoutWindows() const noexcept { return m_layoutWindows; } + const HWND GetWorkAreaWindow() const noexcept { return m_window; } + const GUID GetLayoutId() const noexcept; - ZoneIndexSet GetWindowZoneIndexes(HWND window) const noexcept; + ZoneIndexSet GetWindowZoneIndexes(HWND window) const; - HRESULT MoveSizeEnter(HWND window) noexcept; - HRESULT MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled, bool selectManyZones) noexcept; - HRESULT MoveSizeEnd(HWND window) noexcept; - void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) noexcept; - void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true) noexcept; - bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle) noexcept; - bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle) noexcept; - bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode) noexcept; - void SaveWindowProcessToZoneIndex(HWND window) noexcept; - bool UnsnapWindow(HWND window) noexcept; + void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index); + void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true); + bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle); + bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle); + bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode); - void UpdateActiveZoneSet() noexcept; + void SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend = false); + void UnsnapWindow(HWND window); - void ShowZonesOverlay() noexcept; - void HideZonesOverlay() noexcept; - void FlashZones() noexcept; - void ClearSelectedZones() noexcept; + void UpdateActiveZoneSet(); + void UpdateWindowPositions(); + + void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr); + void HideZonesOverlay(); + void FlashZones(); - void CycleWindows(HWND window, bool reverse) noexcept; - - void LogInitializationError(); + void CycleWindows(HWND window, bool reverse); protected: static LRESULT CALLBACK s_WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept; private: - bool InitWindow(HINSTANCE hinstance) noexcept; - void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept; - void CalculateZoneSet() noexcept; - LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; - ZoneIndexSet ZonesFromPoint(POINT pt) noexcept; - void SetAsTopmostWindow() noexcept; + bool InitWindow(HINSTANCE hinstance); + void InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId); + + void CalculateZoneSet(); + void SetWorkAreaWindowAsTopmost(HWND draggedWindow) noexcept; - HMONITOR m_monitor{}; - FancyZonesUtils::Rect m_workAreaRect{}; + LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; + + const FancyZonesUtils::Rect m_workAreaRect{}; const FancyZonesDataTypes::WorkAreaId m_uniqueId; HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area. - HWND m_windowMoveSize{}; std::unique_ptr m_layout; std::unique_ptr m_layoutWindows; - ZoneIndexSet m_initialHighlightZone; - ZoneIndexSet m_highlightZone; - WPARAM m_keyLast{}; - size_t m_keyCycle{}; std::unique_ptr m_zonesOverlay; }; - -inline std::shared_ptr MakeWorkArea(HINSTANCE hinstance, HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& uniqueId, const FancyZonesDataTypes::WorkAreaId& parentUniqueId) noexcept -{ - auto self = std::make_shared(hinstance, uniqueId); - if (!self->InitWorkAreaRect(monitor)) - { - self->LogInitializationError(); - return nullptr; - } - - if (!self->Init(hinstance, parentUniqueId)) - { - return nullptr; - } - - return self; -} diff --git a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp index b5908eade7..ada45b5d5c 100644 --- a/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp +++ b/src/modules/fancyzones/FancyZonesLib/ZonesOverlay.cpp @@ -70,7 +70,7 @@ D2D1_COLOR_F ZonesOverlay::ConvertColor(COLORREF color) D2D1_RECT_F ZonesOverlay::ConvertRect(RECT rect) { - return D2D1::RectF((float)rect.left + 0.5f, (float)rect.top + 0.5f, (float)rect.right - 0.5f, (float)rect.bottom - 0.5f); + return D2D1::RectF(rect.left + 0.5f, rect.top + 0.5f, rect.right - 0.5f, rect.bottom - 0.5f); } ZonesOverlay::ZonesOverlay(HWND window) @@ -174,7 +174,7 @@ ZonesOverlay::RenderResult ZonesOverlay::Render() { textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); - m_renderTarget->DrawTextW(idStr.c_str(), (UINT32)idStr.size(), textFormat, drawableRect.rect, textBrush); + m_renderTarget->DrawTextW(idStr.c_str(), static_cast(idStr.size()), textFormat, drawableRect.rect, textBrush); } if (textBrush) diff --git a/src/modules/fancyzones/FancyZonesLib/util.cpp b/src/modules/fancyzones/FancyZonesLib/util.cpp index 151e5d9220..4b4ccc40cb 100644 --- a/src/modules/fancyzones/FancyZonesLib/util.cpp +++ b/src/modules/fancyzones/FancyZonesLib/util.cpp @@ -248,32 +248,6 @@ namespace FancyZonesUtils return closestIdx; } - RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept - { - LONG deltaX = 0, deltaY = 0; - switch (vkCode) - { - case VK_UP: - deltaY = workAreaRect.bottom - workAreaRect.top; - break; - case VK_DOWN: - deltaY = workAreaRect.top - workAreaRect.bottom; - break; - case VK_LEFT: - deltaX = workAreaRect.right - workAreaRect.left; - break; - case VK_RIGHT: - deltaX = workAreaRect.left - workAreaRect.right; - } - - windowRect.left += deltaX; - windowRect.right += deltaX; - windowRect.top += deltaY; - windowRect.bottom += deltaY; - - return windowRect; - } - void SwallowKey(const WORD key) noexcept { INPUT inputKey[1] = {}; diff --git a/src/modules/fancyzones/FancyZonesLib/util.h b/src/modules/fancyzones/FancyZonesLib/util.h index 8a6e28a313..41d1a9b4d8 100644 --- a/src/modules/fancyzones/FancyZonesLib/util.h +++ b/src/modules/fancyzones/FancyZonesLib/util.h @@ -93,7 +93,7 @@ namespace FancyZonesUtils } } - inline BYTE OpacitySettingToAlpha(int opacity) + constexpr inline BYTE OpacitySettingToAlpha(int opacity) { return static_cast(opacity * 2.55); } @@ -168,6 +168,32 @@ namespace FancyZonesUtils return result; } + constexpr RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept + { + LONG deltaX = 0, deltaY = 0; + switch (vkCode) + { + case VK_UP: + deltaY = workAreaRect.bottom - workAreaRect.top; + break; + case VK_DOWN: + deltaY = workAreaRect.top - workAreaRect.bottom; + break; + case VK_LEFT: + deltaX = workAreaRect.right - workAreaRect.left; + break; + case VK_RIGHT: + deltaX = workAreaRect.left - workAreaRect.right; + } + + windowRect.left += deltaX; + windowRect.right += deltaX; + windowRect.top += deltaY; + windowRect.bottom += deltaY; + + return windowRect; + } + UINT GetDpiForMonitor(HMONITOR monitor) noexcept; void OrderMonitors(std::vector>& monitorInfo); @@ -175,7 +201,6 @@ namespace FancyZonesUtils std::optional GuidFromString(const std::wstring& str) noexcept; std::optional GuidToString(const GUID& guid) noexcept; - RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept; size_t ChooseNextZoneByPosition(DWORD vkCode, RECT windowRect, const std::vector& zoneRects) noexcept; void SwallowKey(const WORD key) noexcept; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp index 0071514185..f1365f2079 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp @@ -16,7 +16,7 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) { - m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); + m_hInst = static_cast(GetModuleHandleW(nullptr)); AppZoneHistory::instance().LoadData(); } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp index f979cf7c69..36edcf3867 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp @@ -32,16 +32,16 @@ namespace FancyZonesUnitTests json::JsonArray zonesArray{}; { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(0)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(0)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(0)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(1140)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(1040)); zonesArray.Append(zone); } { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(1140)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(649)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(1140)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(649)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(780)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(391)); zonesArray.Append(zone); diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp index 11d0ec3761..4fbd9c163a 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Layout.Spec.cpp @@ -65,8 +65,8 @@ namespace FancyZonesUnitTests for (const auto& zoneRect : zones) { json::JsonObject zone{}; - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XID, json::value(zoneRect.left)); - zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YID, json::value(zoneRect.top)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::XAxisID, json::value(zoneRect.left)); + zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::YAxisID, json::value(zoneRect.top)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::WidthID, json::value(zoneRect.right - zoneRect.left)); zone.SetNamedValue(NonLocalizable::CustomLayoutsIds::HeightID, json::value(zoneRect.bottom - zoneRect.top)); zonesArray.Append(zone); diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp index f85597c0c6..6f638085e7 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Util.cpp @@ -70,7 +70,7 @@ BOOL RegisterDLLWindowClass(LPCWSTR szClassName, Mocks::HwndCreator* creator) wc.lpszMenuName = NULL; wc.cbClsExtra = 0; wc.cbWndExtra = 0; - wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND; + wc.hbrBackground = reinterpret_cast(COLOR_BACKGROUND); auto regRes = RegisterClassEx(&wc); return regRes; @@ -83,9 +83,9 @@ DWORD WINAPI ThreadProc(LPVOID lpParam) if (!creator) return static_cast(-1); - if (RegisterDLLWindowClass((LPCWSTR)creator->getWindowClassName().c_str(), creator) != 0) + if (RegisterDLLWindowClass(creator->getWindowClassName().c_str(), creator) != 0) { - auto hWnd = CreateWindowEx(0, (LPCWSTR)creator->getWindowClassName().c_str(), (LPCWSTR)creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL); + auto hWnd = CreateWindowEx(0, creator->getWindowClassName().c_str(), creator->getTitle().c_str(), WS_EX_APPWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, nullptr, nullptr, creator->getHInstance(), NULL); SetWindowPos(hWnd, HWND_TOPMOST, 10, 10, 100, 100, SWP_SHOWWINDOW); creator->setHwnd(hWnd); creator->setCondition(true); @@ -130,7 +130,7 @@ namespace Mocks m_conditionFlag = false; std::unique_lock lock(m_mutex); - m_thread = CreateThread(0, NULL, ThreadProc, (LPVOID)this, NULL, NULL); + m_thread = CreateThread(0, NULL, ThreadProc, reinterpret_cast(this), NULL, NULL); m_conditionVar.wait(lock, [this] { return m_conditionFlag; }); return m_hWnd; diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index edfeaf80f4..99d5119cb6 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "Util.h" @@ -20,25 +21,26 @@ namespace FancyZonesUnitTests TEST_CLASS (WorkAreaCreationUnitTests) { - FancyZonesDataTypes::WorkAreaId m_uniqueId; + FancyZonesDataTypes::WorkAreaId m_workAreaId; FancyZonesDataTypes::WorkAreaId m_emptyUniqueId; + FancyZonesUtils::Rect m_workAreaRect{ RECT(0,0,1920,1080) }; HINSTANCE m_hInst{}; HMONITOR m_monitor{}; - TEST_METHOD_INITIALIZE(Init) + TEST_METHOD_INITIALIZE(Init) noexcept { - m_uniqueId.monitorId.deviceId.id = L"DELA026"; - m_uniqueId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; - m_uniqueId.monitorId.serialNumber = L"serial-number"; - m_uniqueId.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(); + m_workAreaId.monitorId.deviceId.id = L"DELA026"; + m_workAreaId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; + m_workAreaId.monitorId.serialNumber = L"serial-number"; + m_workAreaId.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(); AppZoneHistory::instance().LoadData(); AppliedLayouts::instance().LoadData(); DefaultLayouts::instance().LoadData(); } - TEST_METHOD_CLEANUP(CleanUp) + TEST_METHOD_CLEANUP(CleanUp) noexcept { std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); @@ -50,9 +52,9 @@ namespace FancyZonesUnitTests { const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout(); - auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); const auto& layout = workArea->GetLayout(); Assert::IsNotNull(layout.get()); @@ -65,9 +67,9 @@ namespace FancyZonesUnitTests { const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout(); - auto workArea = MakeWorkArea({}, {}, m_uniqueId, m_emptyUniqueId); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); const auto& layout = workArea->GetLayout(); Assert::IsNotNull(layout.get()); @@ -95,15 +97,15 @@ namespace FancyZonesUnitTests .sensitivityRadius = 20, }; - auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, parentUniqueId, m_emptyUniqueId); + auto parentWorkArea = WorkArea::Create(m_hInst, parentUniqueId, m_emptyUniqueId, m_workAreaRect); AppliedLayouts::instance().ApplyLayout(parentUniqueId, layout); - auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, parentUniqueId); + auto actualWorkArea = WorkArea::Create(m_hInst, m_workAreaId, parentUniqueId, m_workAreaRect); Assert::IsNotNull(actualWorkArea->GetLayout().get()); Assert::IsNotNull(actualWorkArea->GetLayoutWindows().get()); - Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_uniqueId)); - const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_uniqueId); + Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_workAreaId)); + const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_workAreaId); Assert::AreEqual(static_cast(layout.type), static_cast(actualLayout.type)); Assert::AreEqual(FancyZonesUtils::GuidToString(layout.uuid).value(), FancyZonesUtils::GuidToString(actualLayout.uuid).value()); @@ -131,9 +133,9 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); // test - auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); Assert::IsNotNull(workArea->GetLayout().get()); @@ -164,9 +166,9 @@ namespace FancyZonesUnitTests DefaultLayouts::instance().LoadData(); // test - auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId); + auto workArea = WorkArea::Create({}, m_workAreaId, m_emptyUniqueId, m_workAreaRect); Assert::IsFalse(workArea == nullptr); - Assert::IsTrue(m_uniqueId == workArea->UniqueId()); + Assert::IsTrue(m_workAreaId == workArea->UniqueId()); Assert::IsNotNull(workArea->GetLayout().get()); @@ -177,226 +179,10 @@ namespace FancyZonesUnitTests } }; - TEST_CLASS (WorkAreaUnitTests) - { - FancyZonesDataTypes::WorkAreaId m_uniqueId; - FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty - - HINSTANCE m_hInst{}; - HMONITOR m_monitor{}; - - TEST_METHOD_INITIALIZE(Init) - { - m_uniqueId.monitorId.deviceId.id = L"DELA026"; - m_uniqueId.monitorId.deviceId.instanceId = L"5&10a58c63&0&UID16777488"; - m_uniqueId.monitorId.serialNumber = L"serial-number"; - m_uniqueId.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value(); - - AppZoneHistory::instance().LoadData(); - AppliedLayouts::instance().LoadData(); - } - - TEST_METHOD_CLEANUP(CleanUp) - { - std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); - std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); - } - - public: - TEST_METHOD (MoveSizeEnter) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto expected = S_OK; - const auto actual = workArea->MoveSizeEnter(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeEnterTwice) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto expected = S_OK; - - workArea->MoveSizeEnter(Mocks::Window()); - const auto actual = workArea->MoveSizeEnter(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeUpdate) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto expected = S_OK; - const auto actual = workArea->MoveSizeUpdate(POINT{ 0, 0 }, true, false); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeUpdatePointNegativeCoordinates) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto expected = S_OK; - const auto actual = workArea->MoveSizeUpdate(POINT{ -10, -10 }, true, false); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeUpdatePointBigCoordinates) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto expected = S_OK; - const auto actual = workArea->MoveSizeUpdate(POINT{ LONG_MAX, LONG_MAX }, true, false); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeEnd) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto window = Mocks::Window(); - workArea->MoveSizeEnter(window); - workArea->MoveSizeUpdate({ 20, 20 }, true, true); - - const auto expected = S_OK; - const auto actual = workArea->MoveSizeEnd(window); - Assert::AreEqual(expected, actual); - - const auto& layoutWindows = workArea->GetLayoutWindows(); - const auto actualZoneIndexSet = layoutWindows->GetZoneIndexSetFromWindow(window); - Assert::IsFalse(std::vector{} == actualZoneIndexSet); - } - - TEST_METHOD (MoveSizeEndDifferentWindows) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto window = Mocks::Window(); - workArea->MoveSizeEnter(window); - - const auto expected = E_INVALIDARG; - const auto actual = workArea->MoveSizeEnd(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (MoveSizeEndWindowNotSet) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto expected = E_INVALIDARG; - const auto actual = workArea->MoveSizeEnd(Mocks::Window()); - - Assert::AreEqual(expected, actual); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexNullptrWindow) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - workArea->SaveWindowProcessToZoneIndex(nullptr); - - const auto actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); - Assert::IsTrue(actualAppZoneHistory.empty()); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAdded) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - auto window = Mocks::WindowCreate(m_hInst); - workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); - - workArea->SaveWindowProcessToZoneIndex(window); - - const auto actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); - Assert::IsTrue(actualAppZoneHistory.empty()); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexNoWindowAddedWithFilledAppZoneHistory) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - const auto window = Mocks::WindowCreate(m_hInst); - const auto processPath = get_process_path(window); - const auto deviceId = workArea->UniqueId(); - const auto& layoutId = workArea->GetLayout()->Id(); - - // fill app zone history map - Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, deviceId, Helpers::GuidToString(layoutId), { 0 })); - Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); - const auto& appHistoryArray1 = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); - Assert::AreEqual((size_t)1, appHistoryArray1.size()); - Assert::IsTrue(std::vector{ 0 } == appHistoryArray1[0].zoneIndexSet); - - // add zone without window - workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); - - workArea->SaveWindowProcessToZoneIndex(window); - Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); - const auto& appHistoryArray2 = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); - Assert::AreEqual((size_t)1, appHistoryArray2.size()); - Assert::IsTrue(std::vector{ 0 } == appHistoryArray2[0].zoneIndexSet); - } - - TEST_METHOD (SaveWindowProcessToZoneIndexWindowAdded) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - auto window = Mocks::WindowCreate(m_hInst); - const auto processPath = get_process_path(window); - const auto deviceId = workArea->UniqueId(); - const auto& layoutId = workArea->GetLayout()->Id(); - - workArea->MoveWindowIntoZoneByIndex(window, 0); - - //fill app zone history map - Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, deviceId, Helpers::GuidToString(layoutId), { 2 })); - Assert::AreEqual((size_t)1, AppZoneHistory::instance().GetFullAppZoneHistory().size()); - const auto& appHistoryArray = AppZoneHistory::instance().GetFullAppZoneHistory().at(processPath); - Assert::AreEqual((size_t)1, appHistoryArray.size()); - Assert::IsTrue(std::vector{ 2 } == appHistoryArray[0].zoneIndexSet); - - workArea->SaveWindowProcessToZoneIndex(window); - - const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory(); - Assert::AreEqual((size_t)1, actualAppZoneHistory.size()); - const auto& expected = workArea->GetLayoutWindows()->GetZoneIndexSetFromWindow(window); - const auto& actual = appHistoryArray[0].zoneIndexSet; - Assert::IsTrue(expected == actual); - } - - TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) - { - auto workArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, m_parentUniqueId); - - auto window = Mocks::WindowCreate(m_hInst); - - int originalWidth = 450; - int originalHeight = 550; - - SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW); - SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX); - - workArea->GetLayout()->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true); - - RECT inZoneRect; - GetWindowRect(window, &inZoneRect); - Assert::AreEqual(originalWidth, (int)inZoneRect.right - (int)inZoneRect.left); - Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top); - } - }; - TEST_CLASS (WorkAreaMoveWindowUnitTests) { const std::wstring m_virtualDesktopIdStr = L"{A998CA86-F08D-4BCA-AED8-77F5C8FC9925}"; - const FancyZonesDataTypes::WorkAreaId m_uniqueId{ + const FancyZonesDataTypes::WorkAreaId m_workAreaId{ .monitorId = { .monitor = Mocks::Monitor(), .deviceId = { @@ -412,7 +198,7 @@ namespace FancyZonesUnitTests FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty HINSTANCE m_hInst{}; - HMONITOR m_monitor{}; + FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) }; void PrepareEmptyLayout() { @@ -429,10 +215,10 @@ namespace FancyZonesUnitTests layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(0)); json::JsonObject workAreaId{}; - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_uniqueId.monitorId.deviceId.id)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_uniqueId.monitorId.deviceId.instanceId)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_uniqueId.monitorId.serialNumber)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_uniqueId.monitorId.deviceId.number)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number)); workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr)); json::JsonObject obj{}; @@ -463,10 +249,10 @@ namespace FancyZonesUnitTests layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(20)); json::JsonObject workAreaId{}; - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_uniqueId.monitorId.deviceId.id)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_uniqueId.monitorId.deviceId.instanceId)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_uniqueId.monitorId.serialNumber)); - workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_uniqueId.monitorId.deviceId.number)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber)); + workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number)); workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr)); json::JsonObject obj{}; @@ -482,13 +268,13 @@ namespace FancyZonesUnitTests AppliedLayouts::instance().LoadData(); } - TEST_METHOD_INITIALIZE(Init) + TEST_METHOD_INITIALIZE(Init) noexcept { AppZoneHistory::instance().LoadData(); AppliedLayouts::instance().LoadData(); } - TEST_METHOD_CLEANUP(CleanUp) + TEST_METHOD_CLEANUP(CleanUp) noexcept { std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); @@ -498,7 +284,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -515,7 +301,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -532,7 +318,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -549,7 +335,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -566,7 +352,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto& layoutWindows = workArea->GetLayoutWindows(); @@ -585,7 +371,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -596,14 +382,14 @@ namespace FancyZonesUnitTests Assert::AreEqual((size_t)1, actualAppZoneHistory.size()); const auto& layoutWindows = workArea->GetLayoutWindows(); - Assert::IsTrue(ZoneIndexSet{ (ZoneIndex)workArea->GetLayout()->Zones().size() - 1 } == layoutWindows->GetZoneIndexSetFromWindow(window)); + Assert::IsTrue(ZoneIndexSet{ static_cast(workArea->GetLayout()->Zones().size() - 1) } == layoutWindows->GetZoneIndexSetFromWindow(window)); } TEST_METHOD (MoveAppliedWindowByIndexNoCycle) { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -621,7 +407,7 @@ namespace FancyZonesUnitTests { // prepare PrepareEmptyLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -638,7 +424,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -655,7 +441,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); // test @@ -672,9 +458,9 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone // test workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_RIGHT, true); @@ -690,9 +476,9 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone // test workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_DOWN, true); @@ -708,9 +494,9 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone // test workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true); @@ -726,7 +512,7 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone @@ -744,10 +530,10 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); const auto& layoutWindows = workArea->GetLayoutWindows(); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window)); // test @@ -763,9 +549,9 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone // test workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_UP, false); @@ -781,9 +567,9 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone // test workArea->ExtendWindowByDirectionAndPosition(window, VK_RIGHT); @@ -799,9 +585,9 @@ namespace FancyZonesUnitTests { // prepare PrepareGridLayout(); - auto workArea = MakeWorkArea(m_hInst, m_uniqueId.monitorId.monitor, m_uniqueId, m_parentUniqueId); + auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto window = Mocks::WindowCreate(m_hInst); - workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone + workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone // test workArea->ExtendWindowByDirectionAndPosition(window, VK_DOWN); @@ -812,5 +598,86 @@ namespace FancyZonesUnitTests const auto& layoutWindows = workArea->GetLayoutWindows(); Assert::IsTrue(ZoneIndexSet{ 0, 2 } == layoutWindows->GetZoneIndexSetFromWindow(window)); } + + TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + constexpr int originalWidth = 450; + constexpr int originalHeight = 550; + + SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW); + SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX); + + workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true); + + RECT inZoneRect; + GetWindowRect(window, &inZoneRect); + + Assert::AreEqual(originalWidth, (int)inZoneRect.right - (int)inZoneRect.left); + Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top); + } + + TEST_METHOD (SnapWindowPropertyTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + const ZoneIndexSet expected = { 1, 2 }; + workArea->SnapWindow(window, expected); + + const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + Assert::AreEqual(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) + { + Assert::AreEqual(expected.at(i), actual.at(i)); + } + } + + TEST_METHOD (SnapAppZoneHistoryTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + const ZoneIndexSet expected = { 1, 2 }; + workArea->SnapWindow(window, expected); + + const auto processPath = get_process_path(window); + const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId); + + Assert::IsTrue(history.has_value()); + Assert::AreEqual(expected.size(), history->zoneIndexSet.size()); + for (int i = 0; i < expected.size(); i++) + { + Assert::AreEqual(expected.at(i), history->zoneIndexSet.at(i)); + } + } + + TEST_METHOD (UnsnapPropertyTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + workArea->SnapWindow(window, { 1, 2 }); + workArea->UnsnapWindow(window); + + const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); + Assert::IsTrue(actual.empty()); + } + + TEST_METHOD (UnsnapAppZoneHistoryTest) + { + const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); + const auto window = Mocks::WindowCreate(m_hInst); + + workArea->SnapWindow(window, { 1, 2 }); + workArea->UnsnapWindow(window); + + const auto processPath = get_process_path(window); + const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId); + + Assert::IsFalse(history.has_value()); + } }; } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp index f0ee4ffd01..987371875f 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/Zone.Spec.cpp @@ -16,7 +16,7 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) { - m_hInst = (HINSTANCE)GetModuleHandleW(nullptr); + m_hInst = static_cast(GetModuleHandleW(nullptr)); } public: diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index f3ee395100..faec50e2aa 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Windows; using System.Windows.Input; using Common.UI; -using FancyZonesEditor.Logs; using FancyZonesEditor.Utils; using ManagedCommon; @@ -55,6 +54,8 @@ namespace FancyZonesEditor public App() { + Logger.InitializeLogger("\\FancyZones\\Editor\\Logs"); + // DebugModeCheck(); NativeThreadCTS = new CancellationTokenSource(); FancyZonesEditorIO = new FancyZonesEditorIO(); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs index d6e38930b3..2bbaa067fe 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasEditorWindow.xaml.cs @@ -4,8 +4,8 @@ using System.Windows; using System.Windows.Input; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs index f55813bf4c..ac9c8ac7c3 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs @@ -159,7 +159,7 @@ namespace FancyZonesEditor public abstract void Move(int delta); } - private class SnappyHelperMagnetic : SnappyHelperBase + private sealed class SnappyHelperMagnetic : SnappyHelperBase { private List magnetZoneSizes; private int freePosition; @@ -220,7 +220,7 @@ namespace FancyZonesEditor } } - private class SnappyHelperNonMagnetic : SnappyHelperBase + private sealed class SnappyHelperNonMagnetic : SnappyHelperBase { public SnappyHelperNonMagnetic(IList zones, int zoneIndex, bool isX, ResizeMode mode, int screenAxisOrigin, int screenAxisSize) : base(zones, zoneIndex, isX, mode, screenAxisOrigin, screenAxisSize) diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs index ba006e2c13..01e17f70b8 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs @@ -8,7 +8,7 @@ using System.Windows.Controls; namespace FancyZonesEditor.Controls { - internal class CustomSliderAutomationPeer : SliderAutomationPeer + internal sealed class CustomSliderAutomationPeer : SliderAutomationPeer { private string name = string.Empty; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs index 6bd3f3b652..4a29c81194 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs @@ -4,8 +4,8 @@ using System; using System.Windows; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj index fa00d92d70..06f630a98d 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj +++ b/src/modules/fancyzones/editor/FancyZonesEditor/FancyZonesEditor.csproj @@ -55,9 +55,9 @@ - - - + + + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs index 749ee5a34d..b71cdecb17 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridData.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Controls; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs index 3b5af0ac1a..d49ad9232e 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/GridEditor.xaml.cs @@ -10,8 +10,8 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs index 7cf7257522..61aad3945b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/LayoutPreview.xaml.cs @@ -7,8 +7,8 @@ using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs index 9645be9f6d..687895021b 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs @@ -11,9 +11,9 @@ using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Input; using Common.UI; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; using FancyZonesEditor.Utils; +using ManagedCommon; using ModernWpf.Controls; namespace FancyZonesEditor diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs index 0545ce6c55..5677fb565f 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Overlay.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs index 4a14b82e44..2ddf1cafaf 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs @@ -12,8 +12,8 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using System.Windows; -using FancyZonesEditor.Logs; using FancyZonesEditor.Models; +using ManagedCommon; namespace FancyZonesEditor.Utils { @@ -175,7 +175,7 @@ namespace FancyZonesEditor.Utils } // custom-layouts.json - private class CanvasInfoWrapper + private sealed class CanvasInfoWrapper { public struct CanvasZoneWrapper { @@ -198,7 +198,7 @@ namespace FancyZonesEditor.Utils } // custom-layouts.json - private class GridInfoWrapper + private sealed class GridInfoWrapper { public int Rows { get; set; } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs index 57cefcf9be..b5d4db9952 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/NativeMethods.cs @@ -9,7 +9,7 @@ using System.Windows.Interop; namespace FancyZonesEditor.Utils { - internal class NativeMethods + internal sealed class NativeMethods { [DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest b/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest index a52bf87d3a..598c47dd41 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest +++ b/src/modules/fancyzones/editor/FancyZonesEditor/app.manifest @@ -40,7 +40,7 @@ - + diff --git a/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp b/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp index 4b940e3206..26bf622520 100644 --- a/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp +++ b/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp @@ -227,7 +227,7 @@ private: auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L""); } - else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK) + else if (UuidToString(&temp_uuid, reinterpret_cast(& uuid_chars)) != RPC_S_OK) { auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L""); @@ -236,7 +236,7 @@ private: if (uuid_chars != nullptr) { pipe_name += std::wstring(uuid_chars); - RpcStringFree((RPC_WSTR*)&uuid_chars); + RpcStringFree(reinterpret_cast(&uuid_chars)); uuid_chars = nullptr; } create_pipe_thread = std::thread(&ImageResizerContextMenuCommand::StartNamedPipeServerAndSendData, this, pipe_name); diff --git a/src/modules/imageresizer/dll/ContextMenuHandler.cpp b/src/modules/imageresizer/dll/ContextMenuHandler.cpp index 6440b9677b..fddf2b4497 100644 --- a/src/modules/imageresizer/dll/ContextMenuHandler.cpp +++ b/src/modules/imageresizer/dll/ContextMenuHandler.cpp @@ -130,7 +130,7 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu, mii.fType = MFT_STRING; mii.dwTypeData = (PWSTR)strResizePictures; mii.fState = MFS_ENABLED; - HICON hIcon = (HICON)LoadImage(g_hInst_imageResizer, MAKEINTRESOURCE(IDI_RESIZE_PICTURES), IMAGE_ICON, 16, 16, 0); + HICON hIcon = static_cast(LoadImage(g_hInst_imageResizer, MAKEINTRESOURCE(IDI_RESIZE_PICTURES), IMAGE_ICON, 16, 16, 0)); if (hIcon) { mii.fMask |= MIIM_BITMAP; @@ -185,7 +185,7 @@ HRESULT CContextMenuHandler::GetCommandString(UINT_PTR idCmd, UINT uType, _In_ U { if (uType == GCS_VERBW) { - wcscpy_s((LPWSTR)pszName, cchMax, RESIZE_PICTURES_VERBW); + wcscpy_s(reinterpret_cast(pszName), cchMax, RESIZE_PICTURES_VERBW); } } else @@ -211,7 +211,7 @@ HRESULT CContextMenuHandler::InvokeCommand(_In_ CMINVOKECOMMANDINFO* pici) } else if (fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW)) { - if (wcscmp(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, RESIZE_PICTURES_VERBW) == 0) + if (wcscmp((reinterpret_cast(pici))->lpVerbW, RESIZE_PICTURES_VERBW) == 0) { hr = ResizePictures(pici, nullptr); } @@ -230,7 +230,7 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte // Set the application path based on the location of the dll std::wstring path = get_module_folderpath(g_hInst_imageResizer); path = path + L"\\PowerToys.ImageResizer.exe"; - LPTSTR lpApplicationName = (LPTSTR)path.c_str(); + LPTSTR lpApplicationName = &path[0]; // Create an anonymous pipe to stream filenames SECURITY_ATTRIBUTES sa; HANDLE hReadPipe; diff --git a/src/modules/imageresizer/tests/ImageResizerUITest.csproj b/src/modules/imageresizer/tests/ImageResizerUITest.csproj index e914943fca..18dacef1ff 100644 --- a/src/modules/imageresizer/tests/ImageResizerUITest.csproj +++ b/src/modules/imageresizer/tests/ImageResizerUITest.csproj @@ -49,11 +49,11 @@ - - - - - - + + + + + + diff --git a/src/modules/imageresizer/tests/Test/AssertEx.cs b/src/modules/imageresizer/tests/Test/AssertEx.cs index 5c05ecec44..7961b6f22f 100644 --- a/src/modules/imageresizer/tests/Test/AssertEx.cs +++ b/src/modules/imageresizer/tests/Test/AssertEx.cs @@ -75,7 +75,7 @@ namespace ImageResizer.Test return raisedEvent; } - public class RaisedEvent + public sealed class RaisedEvent { public RaisedEvent(object sender, TArgs args) { diff --git a/src/modules/imageresizer/ui/ImageResizerUI.csproj b/src/modules/imageresizer/ui/ImageResizerUI.csproj index b9fd21cc61..fc3daa760a 100644 --- a/src/modules/imageresizer/ui/ImageResizerUI.csproj +++ b/src/modules/imageresizer/ui/ImageResizerUI.csproj @@ -53,12 +53,10 @@ - - - - - 17.2.3 - + + + + diff --git a/src/modules/interface/powertoy_module_interface.h b/src/modules/interface/powertoy_module_interface.h index 723ef8fb1c..db49afd582 100644 --- a/src/modules/interface/powertoy_module_interface.h +++ b/src/modules/interface/powertoy_module_interface.h @@ -125,6 +125,10 @@ public: return powertoys_gpo::gpo_rule_configured_not_configured; } + // Some actions like PastePlain generate new inputs, which we don't want to catch again. + // The flag was purposefully chose to not collide with other keyboard manager flags. + const static inline ULONG_PTR CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG = 0x110; + protected: HANDLE CreateDefaultEvent(const wchar_t* eventName) { diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx index eeddf004a0..a7cb9495bd 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/Resources.resx @@ -169,7 +169,7 @@ Target App: - Warning: The follow keys do not have assignments: + Warning: The following keys do not have assignments: Key on a keyboard diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp index 3de686b6e0..79ffcf0152 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp @@ -29,9 +29,9 @@ namespace BufferValidationHelpers if (selectedKeyCode != -1) { // Check if the value being set is the same as the other column - if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0) + if (remapBuffer[rowIndex].first[std::abs(colIndex - 1)].index() == 0) { - DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]); + DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(colIndex - 1)]); if (otherColumnKeyCode == selectedKeyCode || IsKeyRemappingToItsCombinedKey(selectedKeyCode, otherColumnKeyCode)) { errorType = ShortcutErrorType::MapToSameKey; @@ -189,7 +189,7 @@ namespace BufferValidationHelpers // If the user tries to set an action key check if all drop down menus after this are empty if it is not the first key. // If it is a hybrid control, this can be done even on the first key bool isClear = true; - for (int i = dropDownIndex + 1; i < (int)dropDownCount; i++) + for (int i = dropDownIndex + 1; i < static_cast(dropDownCount); i++) { if (selectedCodes[i] != -1) { @@ -244,9 +244,9 @@ namespace BufferValidationHelpers if (tempShortcut.index() == 1) { // If shortcut to shortcut - if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1) + if (remapBuffer[rowIndex].first[std::abs(colIndex - 1)].index() == 1) { - auto& shortcut = std::get(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]); + auto& shortcut = std::get(remapBuffer[rowIndex].first[std::abs(colIndex - 1)]); if (shortcut == std::get(tempShortcut) && EditorHelpers::IsValidShortcut(shortcut) && EditorHelpers::IsValidShortcut(std::get(tempShortcut))) { errorType = ShortcutErrorType::MapToSameShortcut; @@ -258,9 +258,9 @@ namespace BufferValidationHelpers else { // If key to key - if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 0) + if (remapBuffer[rowIndex].first[std::abs(colIndex - 1)].index() == 0) { - DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]); + DWORD otherColumnKeyCode = std::get(remapBuffer[rowIndex].first[std::abs(colIndex - 1)]); DWORD shortcutKeyCode = std::get(tempShortcut); if ((otherColumnKeyCode == shortcutKeyCode || IsKeyRemappingToItsCombinedKey(otherColumnKeyCode, shortcutKeyCode)) && otherColumnKeyCode != NULL && shortcutKeyCode != NULL) { diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp index 1230a25f74..2c4f2c681c 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp @@ -137,14 +137,14 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan windowClass.lpfnWndProc = EditKeyboardWindowProc; windowClass.hInstance = hInst; windowClass.lpszClassName = szWindowClass; - windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW); - windowClass.hIcon = (HICON)LoadImageW( + windowClass.hbrBackground = reinterpret_cast(COLOR_WINDOW); + windowClass.hIcon = static_cast(LoadImageW( windowClass.hInstance, MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON), IMAGE_ICON, 48, 48, - LR_DEFAULTCOLOR); + LR_DEFAULTCOLOR)); if (RegisterClassEx(&windowClass) == NULL) { @@ -433,7 +433,7 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar // To avoid UI elements overlapping on making the window smaller enforce a minimum window size case WM_GETMINMAXINFO: { - LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; + LPMINMAXINFO mmi = reinterpret_cast(lParam); float minWidth = EditorConstants::MinimumEditKeyboardWindowWidth; float minHeight = EditorConstants::MinimumEditKeyboardWindowHeight; DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp index 0350cc590a..d312e0091a 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp @@ -75,14 +75,14 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa windowClass.lpfnWndProc = EditShortcutsWindowProc; windowClass.hInstance = hInst; windowClass.lpszClassName = szWindowClass; - windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW); - windowClass.hIcon = (HICON)LoadImageW( + windowClass.hbrBackground = reinterpret_cast(COLOR_WINDOW); + windowClass.hIcon = static_cast(LoadImageW( windowClass.hInstance, MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON), IMAGE_ICON, 48, 48, - LR_DEFAULTCOLOR); + LR_DEFAULTCOLOR)); if (RegisterClassEx(&windowClass) == NULL) { MessageBox(NULL, GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORMESSAGE).c_str(), GET_RESOURCE_STRING(IDS_REGISTERCLASSFAILED_ERRORTITLE).c_str(), NULL); @@ -199,7 +199,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa StackPanel tableHeader = StackPanel(); tableHeader.Orientation(Orientation::Horizontal); tableHeader.Margin({ 10, 0, 0, 10 }); - auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + (double)EditorConstants::ShortcutArrowColumnWidth); + auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + static_cast(EditorConstants::ShortcutArrowColumnWidth)); tableHeader.Children().Append(originalShortcutContainer.as()); auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, EditorConstants::ShortcutTargetColumnWidth); tableHeader.Children().Append(newShortcutHeaderContainer.as()); @@ -386,7 +386,7 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa // To avoid UI elements overlapping on making the window smaller enforce a minimum window size case WM_GETMINMAXINFO: { - LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; + LPMINMAXINFO mmi = reinterpret_cast(lParam); float minWidth = EditorConstants::MinimumEditShortcutsWindowWidth; float minHeight = EditorConstants::MinimumEditShortcutsWindowHeight; DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp index fad1aa4a26..c847ad2a1b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDropDownControl.cpp @@ -326,7 +326,7 @@ void KeyDropDownControl::AddDropDown(StackPanel& table, StackPanel row, Variable keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->SetSelectionHandler(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); // Update accessible name - SetAccessibleNameForComboBox(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox(), (int)keyDropDownControlObjects.size()); + SetAccessibleNameForComboBox(keyDropDownControlObjects[keyDropDownControlObjects.size() - 1]->GetComboBox(), static_cast(keyDropDownControlObjects.size())); } // Function to get the list of key codes from the shortcut combo box stack panel @@ -421,8 +421,12 @@ void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel tabl } } +// Disable 26497 this function should be evaluated at compile time +#pragma warning(push) +#pragma warning(disable : 26497) // Get number of selected keys. Do not count -1 and 0 values as they stand for Not selected and None int KeyDropDownControl::GetNumberOfSelectedKeys(std::vector keyCodes) { return (int)std::count_if(keyCodes.begin(), keyCodes.end(), [](int32_t a) { return a != -1 && a != 0; }); -} \ No newline at end of file +} +#pragma warning(pop) \ No newline at end of file diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp index f41e767f6c..f45c3d1952 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/ShortcutControl.cpp @@ -254,7 +254,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vectorGetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size()); + UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, static_cast(keyboardRemapControlObjects.size())); // Set the shortcut text if the two vectors are not empty (i.e. default args) if (EditorHelpers::IsValidShortcut(originalKeys) && !(newKeys.index() == 0 && std::get(newKeys) == NULL) && !(newKeys.index() == 1 && !EditorHelpers::IsValidShortcut(std::get(newKeys)))) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp index a58f8b8c36..b83e71906b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/SingleKeyRemapControl.cpp @@ -206,7 +206,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve } // Set accessible names - UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, (int)keyboardRemapControlObjects.size()); + UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, static_cast(keyboardRemapControlObjects.size())); } // Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp index c7ec4a9816..fe68461559 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.cpp @@ -141,7 +141,7 @@ bool XamlBridge::NavigateFocus(MSG* msg) } // Function to run the message loop for the xaml island window -int XamlBridge::MessageLoop() +WPARAM XamlBridge::MessageLoop() { MSG msg = {}; HRESULT hr = S_OK; @@ -160,12 +160,12 @@ int XamlBridge::MessageLoop() } Logger::trace("XamlBridge::MessageLoop() stopped"); - return (int)msg.wParam; + return msg.wParam; } -static const WPARAM invalidKey = (WPARAM)-1; +static const WPARAM invalidKey = 0xFFFFFFFFFFFFFFFF; -WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) +constexpr WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) { auto key = invalidKey; if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Last || reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First) diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h index bb01e4961b..20453bd082 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/XamlBridge.h @@ -5,7 +5,7 @@ class XamlBridge { public: // Function to run the message loop for the xaml island window - int MessageLoop(); + WPARAM MessageLoop(); // Constructor XamlBridge(HWND parent) : diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp index f2ee2447f3..be2a8cc02b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.cpp @@ -66,11 +66,11 @@ namespace KeyboardEventHandlers { if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(target), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } else { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(target), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); } } else @@ -79,7 +79,7 @@ namespace KeyboardEventHandlers Shortcut targetShortcut = std::get(it->second); if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); i++; Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); // Dummy key is not required here since SetModifierKeyEvents will only add key-up events for the modifiers here, and the action key key-up is already sent before it @@ -88,7 +88,7 @@ namespace KeyboardEventHandlers { // Dummy key is not required here since SetModifierKeyEvents will only add key-down events for the modifiers here, and the action key key-down is already sent after it Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); i++; } } @@ -236,13 +236,13 @@ namespace KeyboardEventHandlers memset(keyEventList, 0, sizeof(keyEventList)); int i = 0; Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } else { // Dummy key, key up for all the original shortcut modifier keys and key down for all the new shortcut keys but common keys in each are not repeated - key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * (size_t)commonKeys); + key_count = KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE + (src_size - 1) + (dest_size) - (2 * static_cast(commonKeys)); keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); @@ -255,7 +255,7 @@ namespace KeyboardEventHandlers // Set new shortcut key down state Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } @@ -295,14 +295,14 @@ namespace KeyboardEventHandlers // Set target key down state if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } // Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL) { - ResetIfModifierKeyForLowerLevelKeyHandlers(ii, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), data->lParam->vkCode); + ResetIfModifierKeyForLowerLevelKeyHandlers(ii,static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), data->lParam->vkCode); } } @@ -313,7 +313,7 @@ namespace KeyboardEventHandlers state.SetActivatedApp(*activatedApp); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; @@ -350,7 +350,7 @@ namespace KeyboardEventHandlers else { // release all new shortcut keys except the common modifiers and add all original shortcut modifiers except the common ones, and dummy key - key_count = (dest_size - 1) + (src_size - 2) - (2 * (size_t)commonKeys) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE; + key_count = (dest_size - 1) + (src_size - 2) - (2 * static_cast(commonKeys)) + KeyboardManagerConstants::DUMMY_KEY_EVENT_SIZE; } // If the target shortcut's action key is pressed, then it should be released @@ -368,7 +368,7 @@ namespace KeyboardEventHandlers int i = 0; if (isActionKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode); @@ -407,7 +407,7 @@ namespace KeyboardEventHandlers int i = 0; if (std::get(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } @@ -432,7 +432,7 @@ namespace KeyboardEventHandlers // key count can be 0 if both shortcuts have same modifiers and the action key is not held down. delete will throw an error if keyEventList is empty if (key_count > 0) { - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; } return 1; @@ -457,14 +457,14 @@ namespace KeyboardEventHandlers memset(keyEventList, 0, sizeof(keyEventList)); if (remapToShortcut) { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -478,7 +478,7 @@ namespace KeyboardEventHandlers { keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else if (std::get(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) { @@ -497,7 +497,7 @@ namespace KeyboardEventHandlers { keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD,static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); } else { @@ -512,7 +512,7 @@ namespace KeyboardEventHandlers // Release new key state int i = 0; - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(Helpers::FilterArtificialKeys(std::get(it->second.targetShortcut))), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; // Set original shortcut key down state except the action key @@ -534,7 +534,7 @@ namespace KeyboardEventHandlers } } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -586,23 +586,23 @@ namespace KeyboardEventHandlers if (newRemapping.RemapToKey()) { DWORD to = std::get<0>(newRemapping.targetShortcut); - bool isLastKeyStillPressed = ii.GetVirtualKeyState((WORD)from.actionKey); + bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast(from.actionKey)); key_count = static_cast(from.Size()) - 1 + 1 + (isLastKeyStillPressed ? 1 : 0); keyEventList = new INPUT[key_count](); memset(keyEventList, 0, sizeof(keyEventList)); int i = 0; Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); - if (ii.GetVirtualKeyState((WORD)from.actionKey)) + if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)from.actionKey, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)to, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(to), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); }else { Shortcut to = std::get(newRemapping.targetShortcut); - bool isLastKeyStillPressed = ii.GetVirtualKeyState((WORD)from.actionKey); + bool isLastKeyStillPressed = ii.GetVirtualKeyState(static_cast(from.actionKey)); size_t temp_key_count_calculation = static_cast(from.Size()) - 1; temp_key_count_calculation += static_cast(to.Size()) - 1; @@ -612,14 +612,14 @@ namespace KeyboardEventHandlers int i = 0; Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to); - if (ii.GetVirtualKeyState((WORD)from.actionKey)) + if (ii.GetVirtualKeyState(static_cast(from.actionKey))) { // If the action key from the last shortcut is still being pressed, release it. - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)from.actionKey, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(from.actionKey), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from); - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)to.actionKey, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); newRemapping.isShortcutInvoked = true; } @@ -636,7 +636,7 @@ namespace KeyboardEventHandlers else { // Key up for all new shortcut keys, key down for original shortcut modifiers and current key press but common keys aren't repeated - key_count = (dest_size) + (src_size - 1) - (2 * (size_t)commonKeys); + key_count = (dest_size) + (src_size - 1) - (2 * static_cast(commonKeys)); // If the target shortcut's action key is pressed, then it should be released and original shortcut's action key should be set bool isActionKeyPressed = false; @@ -653,7 +653,7 @@ namespace KeyboardEventHandlers int i = 0; if (isActionKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(std::get(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } Helpers::SetModifierKeyEvents(std::get(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); @@ -664,12 +664,12 @@ namespace KeyboardEventHandlers // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again if (isActionKeyPressed) { - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), 0, 0); i++; // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu @@ -686,7 +686,7 @@ namespace KeyboardEventHandlers state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -730,7 +730,7 @@ namespace KeyboardEventHandlers if (isRemapToDisable && isOriginalActionKeyPressed) { // Set original action key - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD,static_cast(it->first.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); i++; } else @@ -739,7 +739,7 @@ namespace KeyboardEventHandlers } // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut - Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); + Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, static_cast(data->lParam->vkCode), 0, 0); i++; // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu @@ -755,7 +755,7 @@ namespace KeyboardEventHandlers state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); } - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; return 1; } @@ -858,8 +858,8 @@ namespace KeyboardEventHandlers memset(keyEventList, 0, sizeof(keyEventList)); // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts - Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)key, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); - UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, static_cast(key), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); + UINT res = ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; } } diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp index 236fec31d2..c99ce09851 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp @@ -38,7 +38,7 @@ namespace RemappingLogicTests } else { - return (intptr_t)1; + return 1LL; } }); } diff --git a/src/modules/keyboardmanager/common/Helpers.cpp b/src/modules/keyboardmanager/common/Helpers.cpp index a85e585d49..e967d5ce98 100644 --- a/src/modules/keyboardmanager/common/Helpers.cpp +++ b/src/modules/keyboardmanager/common/Helpers.cpp @@ -83,6 +83,25 @@ namespace Helpers case VK_DOWN: case VK_RIGHT: case VK_UP: + case VK_SLEEP: + case VK_MEDIA_NEXT_TRACK: + case VK_MEDIA_PREV_TRACK: + case VK_MEDIA_STOP: + case VK_MEDIA_PLAY_PAUSE: + case VK_VOLUME_MUTE: + case VK_VOLUME_UP: + case VK_VOLUME_DOWN: + case VK_LAUNCH_MEDIA_SELECT: + case VK_LAUNCH_MAIL: + case VK_LAUNCH_APP1: + case VK_LAUNCH_APP2: + case VK_BROWSER_SEARCH: + case VK_BROWSER_HOME: + case VK_BROWSER_BACK: + case VK_BROWSER_FORWARD: + case VK_BROWSER_STOP: + case VK_BROWSER_REFRESH: + case VK_BROWSER_FAVORITES: return true; default: return false; @@ -103,15 +122,15 @@ namespace Helpers // Set wScan to the value from MapVirtualKey as some applications may use the scan code for handling input, for instance, Windows Terminal ignores non-character input which has scancode set to 0. // MapVirtualKey returns 0 if the key code does not correspond to a physical key (such as unassigned/reserved keys). More details at https://github.com/microsoft/PowerToys/pull/7143#issue-498877747 - keyEventArray[index].ki.wScan = (WORD)MapVirtualKey(keyCode, MAPVK_VK_TO_VSC); + keyEventArray[index].ki.wScan = static_cast(MapVirtualKey(keyCode, MAPVK_VK_TO_VSC)); } // Function to set the dummy key events used for remapping shortcuts, required to ensure releasing a modifier doesn't trigger another action (For example, Win->Start Menu or Alt->Menu bar) void SetDummyKeyEvent(LPINPUT keyEventArray, int& index, ULONG_PTR extraInfo) { - SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, 0, extraInfo); + SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(KeyboardManagerConstants::DUMMY_KEY), 0, extraInfo); index++; - SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)KeyboardManagerConstants::DUMMY_KEY, KEYEVENTF_KEYUP, extraInfo); + SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(KeyboardManagerConstants::DUMMY_KEY), KEYEVENTF_KEYUP, extraInfo); index++; } @@ -184,22 +203,22 @@ namespace Helpers // If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetWinKey(winKeyInvoked)), 0, extraInfoFlag); index++; } if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetCtrlKey()), 0, extraInfoFlag); index++; } if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetAltKey()), 0, extraInfoFlag); index++; } if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), 0, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetShiftKey()), 0, extraInfoFlag); index++; } } @@ -210,22 +229,22 @@ namespace Helpers // If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetShiftKey()), KEYEVENTF_KEYUP, extraInfoFlag); index++; } if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetAltKey()), KEYEVENTF_KEYUP, extraInfoFlag); index++; } if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetCtrlKey()), KEYEVENTF_KEYUP, extraInfoFlag); index++; } if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased))) { - Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), KEYEVENTF_KEYUP, extraInfoFlag); + Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, static_cast(shortcutToBeSent.GetWinKey(winKeyInvoked)), KEYEVENTF_KEYUP, extraInfoFlag); index++; } } diff --git a/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp b/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp index 262ced5c7e..97487106ca 100644 --- a/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp +++ b/src/modules/keyboardmanager/common/KeyboardEventHandlers.cpp @@ -18,7 +18,7 @@ namespace KeyboardEventHandlers // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); - ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); + ii.SendVirtualInput(static_cast(key_count), keyEventList, sizeof(INPUT)); delete[] keyEventList; } } diff --git a/src/modules/keyboardmanager/common/MappingConfiguration.cpp b/src/modules/keyboardmanager/common/MappingConfiguration.cpp index ff5827afc0..d2727be708 100644 --- a/src/modules/keyboardmanager/common/MappingConfiguration.cpp +++ b/src/modules/keyboardmanager/common/MappingConfiguration.cpp @@ -296,7 +296,7 @@ bool MappingConfiguration::SaveSettingsToFile() for (const auto& it : singleKeyReMap) { json::JsonObject keys; - keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.first))); + keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring(static_cast(it.first)))); // For key to key remapping if (it.second.index() == 0) diff --git a/src/modules/keyboardmanager/common/Shortcut.cpp b/src/modules/keyboardmanager/common/Shortcut.cpp index c06cdb531a..acc7edde14 100644 --- a/src/modules/keyboardmanager/common/Shortcut.cpp +++ b/src/modules/keyboardmanager/common/Shortcut.cpp @@ -425,23 +425,23 @@ winrt::hstring Shortcut::ToHstringVK() const winrt::hstring output; if (winKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetWinKey(ModifierKey::Both)) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetWinKey(ModifierKey::Both))) + winrt::to_hstring(L";"); } if (ctrlKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetCtrlKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetCtrlKey())) + winrt::to_hstring(L";"); } if (altKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetAltKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetAltKey())) + winrt::to_hstring(L";"); } if (shiftKey != ModifierKey::Disabled) { - output = output + winrt::to_hstring((unsigned int)GetShiftKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetShiftKey())) + winrt::to_hstring(L";"); } if (actionKey != NULL) { - output = output + winrt::to_hstring((unsigned int)GetActionKey()) + winrt::to_hstring(L";"); + output = output + winrt::to_hstring(static_cast(GetActionKey())) + winrt::to_hstring(L";"); } if (!output.empty()) @@ -592,13 +592,13 @@ bool Shortcut::CheckModifiersKeyboardState(KeyboardManagerInput::InputInterface& } // Helper method for checking if a key is in a range for cleaner code -bool in_range(DWORD key, DWORD a, DWORD b) +constexpr bool in_range(DWORD key, DWORD a, DWORD b) { return (key >= a && key <= b); } // Helper method for checking if a key is equal to a value for cleaner code -bool equals(DWORD key, DWORD a) +constexpr bool equals(DWORD key, DWORD a) { return (key == a); } diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp index 386f9b0420..b692dd1daf 100644 --- a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp +++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp @@ -349,7 +349,7 @@ public: DWORD windowPid; GetWindowThreadProcessId(nextWindow, &windowPid); - if (windowPid == (DWORD)closePid) + if (windowPid == static_cast(closePid)) ::PostMessage(nextWindow, WM_CLOSE, 0, 0); return true; @@ -401,7 +401,7 @@ void Microsoft_Launcher::parse_hotkey(PowerToysSettings::PowerToyValues& setting try { auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES); - m_use_centralized_keyboard_hook = (bool)jsonPropertiesObject.GetNamedBoolean(JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK); + m_use_centralized_keyboard_hook =jsonPropertiesObject.GetNamedBoolean(JSON_KEY_USE_CENTRALIZED_KEYBOARD_HOOK); } catch (...) { diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj index 1e861536ae..2dde83d9ed 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj index 481bcfafe1..646fca5b83 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj @@ -44,7 +44,7 @@ - + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs index aa25ceb994..bb72b6fa2c 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs @@ -62,7 +62,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter return new Result { ContextData = result, - Title = result.ToString(), + Title = result.ToString(null), IcoPath = _icon_path, Score = 300, SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.copy_to_clipboard, result.QuantityInfo.Name), diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj index 0bd1f34c08..1ad625a6d5 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj @@ -40,7 +40,7 @@ - + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs index 3b76c8cafc..f937e5e9d4 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/SystemPath.cs @@ -6,7 +6,7 @@ using System.Text.RegularExpressions; namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces { - internal class SystemPath + internal sealed class SystemPath { private static readonly Regex WindowsPath = new Regex(@"^([a-zA-Z]:)", RegexOptions.Compiled); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj index 5877bbc7ec..e7733c997e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/Microsoft.Plugin.Folder.UnitTests.csproj @@ -8,11 +8,11 @@ - - - - - + + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj index dfe01a721d..b6b99834a0 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj @@ -53,7 +53,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj index cd8a4d8acb..6742afc6b3 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj index 3ce836398c..9c2c7a0480 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Microsoft.Plugin.Program.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs index 4456928c48..8aaa3dd949 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Programs/Win32Tests.cs @@ -22,7 +22,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Imaging Devices", ExecutableName = "imagingdevices.exe", FullPath = "c:\\program files\\windows photo viewer\\imagingdevices.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -31,7 +31,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Notepad", ExecutableName = "notepad.exe", FullPath = "c:\\windows\\system32\\notepad.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -40,7 +40,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Notepad", ExecutableName = "notepad.exe", FullPath = "c:\\windows\\system32\\notepad.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -50,7 +50,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", Arguments = @"/E:ON /V:ON /K ""C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\\bin\setenv.cmd""", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -60,7 +60,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", Arguments = @"/k ""C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat""", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -69,7 +69,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Command Prompt", ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -78,7 +78,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "File Explorer", ExecutableName = "File Explorer.lnk", FullPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\file explorer.lnk", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -87,7 +87,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "File Explorer", ExecutableName = "explorer.exe", FullPath = "c:\\windows\\explorer.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -96,7 +96,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Wordpad", ExecutableName = "wordpad.exe", FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -105,7 +105,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "WORDPAD", ExecutableName = "WORDPAD.EXE", FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.Win32Application, }; @@ -113,7 +113,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs { Name = "Twitter", FullPath = "c:\\program files (x86)\\google\\chrome\\application\\chrome_proxy.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\chrome apps\\twitter.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\chrome apps\\twitter.lnk", Arguments = " --profile-directory=Default --app-id=jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi", AppType = 0, }; @@ -122,7 +122,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs { Name = "Web page", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge_proxy.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\web page.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\web page.lnk", Arguments = "--profile-directory=Default --app-id=homljgmgpmcbpjbnjpfijnhipfkiclkd", AppType = 0, }; @@ -131,7 +131,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs { Name = "edge - Bing", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge_proxy.exe", - LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\edge - bing.lnk", + LnkFilePath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\edge - bing.lnk", Arguments = " --profile-directory=Default --app-id=aocfnapldcnfbofgmbbllojgocaelgdd", AppType = 0, }; @@ -141,7 +141,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Microsoft Edge", ExecutableName = "msedge.exe", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\msedge.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft edge.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft edge.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -150,7 +150,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Google Chrome", ExecutableName = "chrome.exe", FullPath = "c:\\program files (x86)\\google\\chrome\\application\\chrome.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\google chrome.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\google chrome.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -159,7 +159,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "Proxy App", ExecutableName = "test_proxy.exe", FullPath = "c:\\program files (x86)\\microsoft\\edge\\application\\test_proxy.exe", - LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk", + LnkFilePath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk", AppType = Win32Program.ApplicationType.Win32Application, }; @@ -168,7 +168,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Name = "cmd", ExecutableName = "cmd.exe", FullPath = "c:\\windows\\system32\\cmd.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.RunCommand, // Run command }; @@ -178,7 +178,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs Description = "Cmder: Lovely Console Emulator", ExecutableName = "Cmder.exe", FullPath = "c:\\tools\\cmder\\cmder.exe", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.RunCommand, // Run command }; @@ -188,7 +188,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "Shop Titans.url", FullPath = "steam://rungameid/1258080", ParentDirectory = "C:\\Users\\temp\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Steam", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.InternetShortcutApplication, }; @@ -198,7 +198,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "Shop Titans.url", FullPath = "steam://rungameid/1258080", ParentDirectory = "C:\\Users\\temp\\Desktop", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.InternetShortcutApplication, }; @@ -208,7 +208,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "dummy.appref-ms", FullPath = "C:\\dummy.appref-ms", ParentDirectory = "C:\\", - LnkResolvedPath = null, + LnkFilePath = null, AppType = Win32Program.ApplicationType.ApprefApplication, }; @@ -218,7 +218,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "application.lnk", FullPath = "C:\\application.lnk", ParentDirectory = "C:\\", - LnkResolvedPath = "C:\\application.lnk", + LnkFilePath = "C:\\application.lnk", AppType = Win32Program.ApplicationType.ShortcutApplication, }; @@ -228,7 +228,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "application.lnk", FullPath = "C:\\dummy\\folder", ParentDirectory = "C:\\dummy\\", - LnkResolvedPath = "C:\\tools\\application.lnk", + LnkFilePath = "C:\\tools\\application.lnk", AppType = Win32Program.ApplicationType.Folder, }; @@ -238,7 +238,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs ExecutableName = "application.lnk", FullPath = "C:\\dummy\\file.pdf", ParentDirectory = "C:\\dummy\\", - LnkResolvedPath = "C:\\tools\\application.lnk", + LnkFilePath = "C:\\tools\\application.lnk", AppType = Win32Program.ApplicationType.GenericFile, }; @@ -303,7 +303,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs } [TestMethod] - public void DedupFunctionMustRemoveDuplicatesForExeExtensionsWithoutLnkResolvedPath() + public void DedupFunctionMustRemoveDuplicatesForExeExtensionsWithoutLnkFilePath() { // Arrange List prgms = new List @@ -317,7 +317,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs // Assert Assert.AreEqual(1, apps.Count); - Assert.IsTrue(!string.IsNullOrEmpty(apps[0].LnkResolvedPath)); + Assert.IsTrue(!string.IsNullOrEmpty(apps[0].LnkFilePath)); } [TestMethod] @@ -592,12 +592,27 @@ namespace Microsoft.Plugin.Program.UnitTests.Programs var mock = new Mock(); StringMatcher.Instance = new StringMatcher(); + // Act + var result = _chrome.Result("chrome", string.Empty, mock.Object); + + // Assert + // Using Ordinal since this is used internally + Assert.IsTrue(result.Title.Equals(_chrome.Name, StringComparison.Ordinal)); + Assert.IsFalse(result.Title.Equals(_chrome.Description, StringComparison.Ordinal)); + } + + [TestMethod] + public void RunCommandsShouldSetExecutableNameAsTitleWhileCreatingResult() + { + var mock = new Mock(); + StringMatcher.Instance = new StringMatcher(); + // Act var result = _cmderRunCommand.Result("cmder", string.Empty, mock.Object); // Assert // Using Ordinal since this is used internally - Assert.IsTrue(result.Title.Equals(_cmderRunCommand.Name, StringComparison.Ordinal)); + Assert.IsTrue(result.Title.Equals(_cmderRunCommand.ExecutableName, StringComparison.Ordinal)); Assert.IsFalse(result.Title.Equals(_cmderRunCommand.Description, StringComparison.Ordinal)); } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs index 5ae07c55ad..0d4aafb094 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs @@ -331,7 +331,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage ExecutableName = "path.exe", ParentDirectory = "directory", FullPath = "directory\\path.exe", - LnkResolvedPath = "directory\\path.lnk", // This must be equal for lnk applications + LnkFilePath = "directory\\path.lnk", // This must be equal for lnk applications }; win32ProgramRepository.Add(item); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs index 2aeb1c3f87..146ee7556c 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs @@ -13,6 +13,7 @@ using Microsoft.Plugin.Program.Programs; using Microsoft.Plugin.Program.Storage; using Wox.Infrastructure.Storage; using Wox.Plugin; +using Wox.Plugin.Common; using Stopwatch = Wox.Infrastructure.Stopwatch; namespace Microsoft.Plugin.Program @@ -30,6 +31,8 @@ namespace Microsoft.Plugin.Program internal static ProgramPluginSettings Settings { get; set; } + internal static readonly ShellLocalization ShellLocalizationHelper = new(); + public string Name => Properties.Resources.wox_plugin_program_plugin_name; public string Description => Properties.Resources.wox_plugin_program_plugin_description; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs index 7fab608c86..d78e758b1e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramPluginSettings.cs @@ -17,6 +17,8 @@ namespace Microsoft.Plugin.Program public List ProgramSuffixes { get; set; } = new List() { "bat", "appref-ms", "exe", "lnk", "url" }; + public List RunCommandSuffixes { get; set; } = new List() { "bat", "appref-ms", "exe", "lnk", "url", "cpl", "msc" }; + public bool EnableStartMenuSource { get; set; } = true; public bool EnableDesktopSource { get; set; } = true; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs index ec57218705..22b8326c68 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWP.cs @@ -36,6 +36,9 @@ namespace Microsoft.Plugin.Program.Programs public string Location { get; set; } + // Localized path based on windows display language + public string LocationLocalized { get; set; } + public IList Apps { get; private set; } public PackageVersion Version { get; set; } @@ -57,6 +60,7 @@ namespace Microsoft.Plugin.Program.Programs public void InitializeAppInfo(string installedLocation) { Location = installedLocation; + LocationLocalized = Main.ShellLocalizationHelper.GetLocalizedPath(installedLocation); var path = Path.Combine(installedLocation, "AppxManifest.xml"); var namespaces = XmlNamespaces(path); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs index 01e1694e23..7543e2af10 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs @@ -55,6 +55,9 @@ namespace Microsoft.Plugin.Program.Programs public string Location => Package.Location; + // Localized path based on windows display language + public string LocationLocalized => Package.LocationLocalized; + public bool Enabled { get; set; } public bool CanRunElevated { get; set; } @@ -119,7 +122,7 @@ namespace Microsoft.Plugin.Program.Programs // Using CurrentCulture since this is user facing var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); - var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, Package.Location); + var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, LocationLocalized); result.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); return result; @@ -436,9 +439,9 @@ namespace Microsoft.Plugin.Program.Programs paths.Add(path); } - if (_scaleFactors.ContainsKey(Package.Version)) + if (_scaleFactors.TryGetValue(Package.Version, out List factors)) { - foreach (var factor in _scaleFactors[Package.Version]) + foreach (var factor in factors) { if (highContrast) { @@ -586,10 +589,10 @@ namespace Microsoft.Plugin.Program.Programs internal void LogoPathFromUri(string uri, Theme theme) { - // all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets - // windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx - // windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size - // windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx + // all https://learn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets + // windows 10 https://msdn.microsoft.com/library/windows/apps/dn934817.aspx + // windows 8.1 https://msdn.microsoft.com/library/windows/apps/hh965372.aspx#target_size + // windows 8 https://msdn.microsoft.com/library/windows/apps/br211475.aspx string path; bool isLogoUriSet; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index f9d31b6ece..9f22fd0382 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -20,7 +20,6 @@ using Microsoft.Win32; using Wox.Infrastructure; using Wox.Infrastructure.FileSystemHelper; using Wox.Plugin; -using Wox.Plugin.Common; using Wox.Plugin.Logger; using DirectoryWrapper = Wox.Infrastructure.FileSystemHelper.DirectoryWrapper; @@ -38,24 +37,35 @@ namespace Microsoft.Plugin.Program.Programs public string Name { get; set; } + // Localized name based on windows display language + public string NameLocalized { get; set; } = string.Empty; + public string UniqueIdentifier { get; set; } public string IcoPath { get; set; } + public string Description { get; set; } = string.Empty; + + // Path of app executable or lnk target executable public string FullPath { get; set; } - public string LnkResolvedPath { get; set; } - - public string LnkResolvedExecutableName { get; set; } - - // Localized name based on windows display language - public string LocalizedName { get; set; } = string.Empty; + // Localized path based on windows display language + public string FullPathLocalized { get; set; } = string.Empty; public string ParentDirectory { get; set; } public string ExecutableName { get; set; } - public string Description { get; set; } = string.Empty; + // Localized executable name based on windows display language + public string ExecutableNameLocalized { get; set; } = string.Empty; + + // Path to the lnk file on LnkProgram + public string LnkFilePath { get; set; } + + public string LnkResolvedExecutableName { get; set; } + + // Localized path based on windows display language + public string LnkResolvedExecutableNameLocalized { get; set; } = string.Empty; public bool Valid { get; set; } @@ -81,7 +91,7 @@ namespace Microsoft.Plugin.Program.Programs private const string ShortcutExtension = "lnk"; private const string ApplicationReferenceExtension = "appref-ms"; private const string InternetShortcutExtension = "url"; - private static readonly HashSet ExecutableApplicationExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "exe", "bat", "bin", "com", "msc", "msi", "cmd", "ps1", "job", "msp", "mst", "sct", "ws", "wsh", "wsf" }; + private static readonly HashSet ExecutableApplicationExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { "exe", "bat", "bin", "com", "cpl", "msc", "msi", "cmd", "ps1", "job", "msp", "mst", "sct", "ws", "wsh", "wsf" }; private const string ProxyWebApp = "_proxy.exe"; private const string AppIdArgument = "--app-id"; @@ -102,11 +112,13 @@ namespace Microsoft.Plugin.Program.Programs private int Score(string query) { var nameMatch = StringMatcher.FuzzySearch(query, Name); - var locNameMatch = StringMatcher.FuzzySearch(query, LocalizedName); + var locNameMatch = StringMatcher.FuzzySearch(query, NameLocalized); var descriptionMatch = StringMatcher.FuzzySearch(query, Description); var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName); + var locExecutableNameMatch = StringMatcher.FuzzySearch(query, ExecutableNameLocalized); var lnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableName); - var score = new[] { nameMatch.Score, locNameMatch.Score, descriptionMatch.Score / 2, executableNameMatch.Score, lnkResolvedExecutableNameMatch.Score }.Max(); + var locLnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableNameLocalized); + var score = new[] { nameMatch.Score, locNameMatch.Score, descriptionMatch.Score / 2, executableNameMatch.Score, locExecutableNameMatch.Score, lnkResolvedExecutableNameMatch.Score, locLnkResolvedExecutableNameMatch.Score }.Max(); return score; } @@ -227,7 +239,7 @@ namespace Microsoft.Plugin.Program.Programs var result = new Result { // To set the title for the result to always be the name of the application - Title = !string.IsNullOrEmpty(LocalizedName) ? LocalizedName : Name, + Title = !string.IsNullOrEmpty(NameLocalized) ? NameLocalized : Name, SubTitle = GetSubtitle(), IcoPath = IcoPath, Score = score, @@ -243,11 +255,18 @@ namespace Microsoft.Plugin.Program.Programs }, }; + // Adjust title of RunCommand result + if (AppType == ApplicationType.RunCommand) + { + result.Title = ExecutableName; + } + result.TitleHighlightData = StringMatcher.FuzzySearch(query, result.Title).MatchData; // Using CurrentCulture since this is user facing var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); - var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, FullPath); + string filePath = !string.IsNullOrEmpty(FullPathLocalized) ? FullPathLocalized : FullPath; + var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, filePath); result.ToolTipData = new ToolTipData(toolTipTitle, toolTipText); return result; @@ -346,7 +365,7 @@ namespace Microsoft.Plugin.Program.Programs { return new ProcessStartInfo { - FileName = LnkResolvedPath ?? FullPath, + FileName = LnkFilePath ?? FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true, Arguments = programArguments, @@ -376,17 +395,19 @@ namespace Microsoft.Plugin.Program.Programs ExecutableName = Path.GetFileName(path), IcoPath = path, - // Localized name based on windows display language - LocalizedName = ShellLocalization.GetLocalizedName(path), - // Using InvariantCulture since this is user facing - FullPath = path.ToLowerInvariant(), + FullPath = path, UniqueIdentifier = path, ParentDirectory = Directory.GetParent(path).FullName, Description = string.Empty, Valid = true, Enabled = true, AppType = ApplicationType.Win32Application, + + // Localized name, path and executable based on windows display language + NameLocalized = Main.ShellLocalizationHelper.GetLocalizedName(path), + FullPathLocalized = Main.ShellLocalizationHelper.GetLocalizedPath(path), + ExecutableNameLocalized = Path.GetFileName(Main.ShellLocalizationHelper.GetLocalizedPath(path)), }; } catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException) @@ -403,7 +424,7 @@ namespace Microsoft.Plugin.Program.Programs } } - private static readonly Regex InternetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run)\/|^com\.epicgames\.launcher:\/\/apps\/", RegexOptions.Compiled); + private static readonly Regex InternetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run|open)\/|^com\.epicgames\.launcher:\/\/apps\/", RegexOptions.Compiled); // This function filters Internet Shortcut programs private static Win32Program InternetShortcutProgram(string path) @@ -462,7 +483,7 @@ namespace Microsoft.Plugin.Program.Programs Name = Path.GetFileNameWithoutExtension(path), ExecutableName = Path.GetFileName(path), IcoPath = iconPath, - FullPath = urlPath.ToLowerInvariant(), + FullPath = urlPath, UniqueIdentifier = path, ParentDirectory = Directory.GetParent(path).FullName, Valid = true, @@ -500,11 +521,13 @@ namespace Microsoft.Plugin.Program.Programs return InvalidProgram; } - program.LnkResolvedPath = program.FullPath; + program.LnkFilePath = program.FullPath; program.LnkResolvedExecutableName = Path.GetFileName(target); + program.LnkResolvedExecutableNameLocalized = Path.GetFileName(Main.ShellLocalizationHelper.GetLocalizedPath(target)); // Using CurrentCulture since this is user facing - program.FullPath = Path.GetFullPath(target).ToLowerInvariant(); + program.FullPath = Path.GetFullPath(target); + program.FullPathLocalized = Main.ShellLocalizationHelper.GetLocalizedPath(target); program.Arguments = ShellLinkHelper.Arguments; @@ -798,7 +821,7 @@ namespace Microsoft.Plugin.Program.Programs private static IEnumerable RegistryAppProgramPaths(IList suffixes) { - // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 + // https://msdn.microsoft.com/library/windows/desktop/ee872121 const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; var paths = new List(); using (var root = Registry.LocalMachine.OpenSubKey(appPaths)) @@ -931,7 +954,7 @@ namespace Microsoft.Plugin.Program.Programs return false; } - // https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121 + // https://msdn.microsoft.com/library/windows/desktop/ee872121 try { var redirectionPath = ReparsePoint.GetTarget(program.FullPath); @@ -988,7 +1011,7 @@ namespace Microsoft.Plugin.Program.Programs // Run commands are always set as AppType "RunCommand" var runCommandSources = new (bool IsEnabled, Func> GetPaths)[] { - (settings.EnablePathEnvironmentVariableSource, () => PathEnvironmentProgramPaths(settings.ProgramSuffixes)), + (settings.EnablePathEnvironmentVariableSource, () => PathEnvironmentProgramPaths(settings.RunCommandSuffixes)), }; var disabledProgramsList = settings.DisabledProgramSources; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs index d0360db3c3..d7eae7008c 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs @@ -150,7 +150,7 @@ namespace Microsoft.Plugin.Program.Storage // Using OrdinalIgnoreCase since this is used internally if (extension.Equals(LnkExtension, StringComparison.OrdinalIgnoreCase)) { - app = GetAppWithSameLnkResolvedPath(path); + app = GetAppWithSameLnkFilePath(path); if (app == null) { // Cancelled links won't have a resolved path. @@ -195,12 +195,12 @@ namespace Microsoft.Plugin.Program.Storage // To mitigate the issue faced (as stated above) when a shortcut application is renamed, the Exe FullPath and executable name must be obtained. // Unlike the rename event args, since we do not have a newPath, we iterate through all the programs and find the one with the same LnkResolved path. - private Programs.Win32Program GetAppWithSameLnkResolvedPath(string lnkResolvedPath) + private Programs.Win32Program GetAppWithSameLnkFilePath(string lnkFilePath) { foreach (Programs.Win32Program app in Items) { // Using Invariant / OrdinalIgnoreCase since we're comparing paths - if (lnkResolvedPath.ToUpperInvariant().Equals(app.LnkResolvedPath, StringComparison.OrdinalIgnoreCase)) + if (lnkFilePath.ToUpperInvariant().Equals(app.LnkFilePath, StringComparison.OrdinalIgnoreCase)) { return app; } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj index 3c56f13b91..7581eeb4fc 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/Microsoft.Plugin.Uri.UnitTests.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj index fd08d18471..9514bf32bf 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker.UnitTests/Microsoft.Plugin.WindowWalker.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs index d7962f0286..4ec3f4025f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchController.cs @@ -101,7 +101,7 @@ namespace Microsoft.Plugin.WindowWalker.Components if (string.IsNullOrWhiteSpace(SearchText)) { - searchMatches = new List(); + searchMatches = AllOpenWindows(snapshotOfOpenWindows); } else { @@ -117,23 +117,16 @@ namespace Microsoft.Plugin.WindowWalker.Components private List FuzzySearchOpenWindows(List openWindows) { List result = new List(); - List searchStrings = new List(); + var searchStrings = new SearchString(searchText, SearchResult.SearchType.Fuzzy); - searchStrings.Add(new SearchString(searchText, SearchResult.SearchType.Fuzzy)); - - foreach (var searchString in searchStrings) + foreach (var window in openWindows) { - foreach (var window in openWindows) - { - var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchString.SearchText); - var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.Process.Name, searchString.SearchText); + var titleMatch = FuzzyMatching.FindBestFuzzyMatch(window.Title, searchStrings.SearchText); + var processMatch = FuzzyMatching.FindBestFuzzyMatch(window.Process.Name, searchStrings.SearchText); - if ((titleMatch.Count != 0 || processMatch.Count != 0) && - window.Title.Length != 0) - { - var temp = new SearchResult(window, titleMatch, processMatch, searchString.SearchType); - result.Add(temp); - } + if ((titleMatch.Count != 0 || processMatch.Count != 0) && window.Title.Length != 0) + { + result.Add(new SearchResult(window, titleMatch, processMatch, searchStrings.SearchType)); } } @@ -142,6 +135,26 @@ namespace Microsoft.Plugin.WindowWalker.Components return result; } + /// + /// Search method that matches all the windows with a title + /// + /// what windows are open + /// Returns search results + private List AllOpenWindows(List openWindows) + { + List result = new List(); + + foreach (var window in openWindows) + { + if (window.Title.Length != 0) + { + result.Add(new SearchResult(window)); + } + } + + return result.OrderBy(w => w.Result.Title).ToList(); + } + /// /// Event args for a window list update event /// diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs index 9d4be2cf33..168f11d2f5 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/SearchResult.cs @@ -80,6 +80,18 @@ namespace Microsoft.Plugin.WindowWalker.Components CalculateScore(); } + /// + /// Initializes a new instance of the class. + /// + internal SearchResult(Window window) + { + Result = window; + SearchMatchesInTitle = new List(); + SearchMatchesInProcessName = new List(); + SearchResultMatchType = SearchType.Empty; + CalculateScore(); + } + /// /// Calculates the score for how closely this window matches the search string /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs index 82a5b83a61..9332eee813 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs @@ -37,8 +37,6 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests [DataTestMethod] [DataRow("test")] - [DataRow("pi(2)")] // Incorrect input, constant is being treated as a function. - [DataRow("e(2)")] [DataRow("[10,10]")] // '[10,10]' is interpreted as array by mages engine public void Interpret_NoResult_WhenCalled(string input) { @@ -74,6 +72,8 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests new object[] { "e*2", 5.43656365691809M }, new object[] { "ln(3)", 1.09861228866810M }, new object[] { "log(3)", 0.47712125471966M }, + new object[] { "log2(3)", 1.58496250072116M }, + new object[] { "log10(3)", 0.47712125471966M }, new object[] { "ln(e)", 1M }, new object[] { "cosh(0)", 1M }, }; @@ -166,6 +166,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests [DataTestMethod] [DataRow("log(3)", true)] [DataRow("ln(3)", true)] + [DataRow("log2(3)", true)] + [DataRow("log10(3)", true)] + [DataRow("log2", false)] + [DataRow("log10", false)] [DataRow("log", false)] [DataRow("ln", false)] [DataRow("ceil(2 * (pi ^ 2))", true)] diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj index 4ffbbeeb28..b7617a8f6f 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs index 56dd89430a..a12744b684 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Globalization; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -14,7 +15,6 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests { [DataTestMethod] [DataRow("=pi(9+)", "Expression wrong or incomplete (Did you forget some parentheses?)")] - [DataRow("=pi(9)", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=pi,", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=log()", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=0xf0x6", "Expression wrong or incomplete (Did you forget some parentheses?)")] @@ -41,7 +41,6 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests [DataTestMethod] [DataRow("pi(9+)")] - [DataRow("pi(9)")] [DataRow("pi,")] [DataRow("log()")] [DataRow("0xf0x6")] @@ -113,5 +112,111 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator.UnitTests Assert.AreEqual(result, "Copy this number to the clipboard"); Assert.AreEqual(resultWithKeyword, "Copy this number to the clipboard"); } + + [DataTestMethod] + [DataRow("pie", "pi * e")] + [DataRow("eln(100)", "e * ln(100)")] + [DataRow("pi(1+1)", "pi * (1+1)")] + [DataRow("2pi", "2 * pi")] + [DataRow("2log10(100)", "2 * log10(100)")] + [DataRow("2(3+4)", "2 * (3+4)")] + [DataRow("sin(pi)cos(pi)", "sin(pi) * cos(pi)")] + [DataRow("log10(100)(2+3)", "log10(100) * (2+3)")] + [DataRow("(1+1)cos(pi)", "(1+1) * cos(pi)")] + [DataRow("(1+1)(2+2)", "(1+1) * (2+2)")] + [DataRow("2(1+1)", "2 * (1+1)")] + [DataRow("pi(1+1)", "pi * (1+1)")] + [DataRow("pilog(100)", "pi * log(100)")] + [DataRow("3log(100)", "3 * log(100)")] + [DataRow("2e", "2 * e")] + [DataRow("(1+1)(3+2)", "(1+1) * (3+2)")] + [DataRow("(1+1)cos(pi)", "(1+1) * cos(pi)")] + [DataRow("sin(pi)cos(pi)", "sin(pi) * cos(pi)")] + [DataRow("2 (1+1)", "2 * (1+1)")] + [DataRow("pi (1+1)", "pi * (1+1)")] + [DataRow("pi log(100)", "pi * log(100)")] + [DataRow("3 log(100)", "3 * log(100)")] + [DataRow("2 e", "2 * e")] + [DataRow("(1+1) (3+2)", "(1+1) * (3+2)")] + [DataRow("(1+1) cos(pi)", "(1+1) * cos(pi)")] + [DataRow("sin (pi) cos(pi)", "sin (pi) * cos(pi)")] + [DataRow("2picos(pi)(1+1)", "2 * pi * cos(pi) * (1+1)")] + [DataRow("pilog(100)log(1000)", "pi * log(100) * log(1000)")] + [DataRow("pipipie", "pi * pi * pi * e")] + [DataRow("(1+1)(3+2)(1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] + [DataRow("(1+1) (3+2) (1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] + public void RightHumanMultiplicationExpressionTransformation(string typedString, string expectedQuery) + { + // Setup + + // Act + var result = CalculateHelper.FixHumanMultiplicationExpressions(typedString); + + // Assert + Assert.AreEqual(expectedQuery, result); + } + + [DataTestMethod] + [DataRow("2(1+1)")] + [DataRow("pi(1+1)")] + [DataRow("pilog(100)")] + [DataRow("3log(100)")] + [DataRow("2e")] + [DataRow("(1+1)(3+2)")] + [DataRow("(1+1)cos(pi)")] + [DataRow("sin(pi)cos(pi)")] + [DataRow("2 (1+1)")] + [DataRow("pi (1+1)")] + [DataRow("pi log(100)")] + [DataRow("3 log(100)")] + [DataRow("2 e")] + [DataRow("(1+1) (3+2)")] + [DataRow("(1+1) cos(pi)")] + [DataRow("sin (pi) cos(pi)")] + [DataRow("2picos(pi)(1+1)")] + [DataRow("pilog(100)log(1000)")] + [DataRow("pipipie")] + [DataRow("(1+1)(3+2)(1+1)(1+1)")] + [DataRow("(1+1) (3+2) (1+1)(1+1)")] + public void NoErrorForHumanMultiplicationExpressions(string typedString) + { + // Setup + Mock
main = new(); + Query expectedQuery = new(typedString); + Query expectedQueryWithKeyword = new("=" + typedString, "="); + + // Act + var result = main.Object.Query(expectedQuery).FirstOrDefault()?.SubTitle; + var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.SubTitle; + + // Assert + Assert.AreEqual("Copy this number to the clipboard", result); + Assert.AreEqual("Copy this number to the clipboard", resultWithKeyword); + } + + [DataTestMethod] + [DataRow("2(1+1)", 4)] + [DataRow("pi(1+1)", 6.2831853072)] + [DataRow("pilog(100)", 6.2831853072)] + [DataRow("3log(100)", 6)] + [DataRow("2e", 5.4365636569)] + [DataRow("(1+1)(3+2)", 10)] + [DataRow("(1+1)cos(pi)", -2)] + [DataRow("log(100)cos(pi)", -2)] + public void RightAnswerForHumanMultiplicationExpressions(string typedString, double answer) + { + // Setup + Mock
main = new(); + Query expectedQuery = new(typedString); + Query expectedQueryWithKeyword = new("=" + typedString, "="); + + // Act + var result = main.Object.Query(expectedQuery).FirstOrDefault()?.Title; + var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.Title; + + // Assert + Assert.AreEqual(answer.ToString(CultureInfo.CurrentCulture), result); + Assert.AreEqual(answer.ToString(CultureInfo.CurrentCulture), resultWithKeyword); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs index e69a5eee05..eed608e24f 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs @@ -49,6 +49,8 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator Replace("log(", "log10(", true, CultureInfo.CurrentCulture). Replace("ln(", "log(", true, CultureInfo.CurrentCulture); + input = CalculateHelper.FixHumanMultiplicationExpressions(input); + var result = _magesEngine.Interpret(input); // This could happen for some incorrect queries, like pi(2) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index ca00f679d3..50b94b1e54 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -12,7 +12,7 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator private static readonly Regex RegValidExpressChar = new Regex( @"^(" + @"%|" + - @"ceil\s*\(|floor\s*\(|exp\s*\(|max\s*\(|min\s*\(|abs\s*\(|log\s*\(|ln\s*\(|sqrt\s*\(|pow\s*\(|" + + @"ceil\s*\(|floor\s*\(|exp\s*\(|max\s*\(|min\s*\(|abs\s*\(|log(?:2|10)?\s*\(|ln\s*\(|sqrt\s*\(|pow\s*\(|" + @"factorial\s*\(|sign\s*\(|round\s*\(|rand\s*\(|" + @"sin\s*\(|cos\s*\(|tan\s*\(|arcsin\s*\(|arccos\s*\(|arctan\s*\(|" + @"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" + @@ -48,5 +48,141 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator return true; } + + public static string FixHumanMultiplicationExpressions(string input) + { + var output = CheckNumberOrConstantThenParenthesisExpr(input); + output = CheckNumberOrConstantThenFunc(output); + output = CheckParenthesisExprThenFunc(output); + output = CheckParenthesisExprThenParenthesisExpr(output); + output = CheckNumberThenConstant(output); + output = CheckConstantThenConstant(output); + return output; + } + + /* + * num (exp) + * const (exp) + */ + private static string CheckNumberOrConstantThenParenthesisExpr(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+|pi|e)\s*(\()", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + /* + * num func + * const func + */ + private static string CheckNumberOrConstantThenFunc(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+|pi|e)\s*([a-zA-Z]+[0-9]*\s*\()", m => + { + if (input[m.Index] == 'e' && input[m.Index + 1] == 'x' && input[m.Index + 2] == 'p') + { + return m.Value; + } + + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + /* + * (exp) func + * func func + */ + private static string CheckParenthesisExprThenFunc(string input) + { + var p = @"(\))\s*([a-zA-Z]+[0-9]*\s*\()"; + var r = "$1 * $2"; + return Regex.Replace(input, p, r); + } + + /* + * (exp) (exp) + * func (exp) + */ + private static string CheckParenthesisExprThenParenthesisExpr(string input) + { + var p = @"(\))\s*(\()"; + var r = "$1 * $2"; + return Regex.Replace(input, p, r); + } + + /* + * num const + */ + private static string CheckNumberThenConstant(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+)\s*(pi|e)", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + /* + * const const + */ + private static string CheckConstantThenConstant(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(pi|e)\s*(pi|e)", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj index 898d245cb9..cfe563edf8 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj @@ -44,7 +44,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj index 06761565da..f3f1408595 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj @@ -48,8 +48,8 @@ - - + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj index 801aefb986..6e611ea7af 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj @@ -46,13 +46,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj index 0502457f1f..dbe62e3a3e 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry.UnitTest/Microsoft.PowerToys.Run.Plugin.Registry.UnitTests.csproj @@ -20,9 +20,9 @@ - - - + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj index 9aff6441da..97ba348518 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj index 3e244cdfd3..64a2fa7d09 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System.UnitTests/Microsoft.PowerToys.Run.Plugin.System.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs index 7e337c2bf5..9278e4941a 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/Commands.cs @@ -5,14 +5,10 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Runtime; -using System.Windows; -using System.Windows.Interop; using Microsoft.PowerToys.Run.Plugin.System.Properties; using Wox.Infrastructure; using Wox.Plugin; using Wox.Plugin.Common.Win32; -using Wox.Plugin.Logger; namespace Microsoft.PowerToys.Run.Plugin.System.Components { @@ -37,11 +33,13 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components /// Returns a list with all system command results /// /// Value indicating if the system is booted in uefi mode + /// Value indicating if we should show two results for Recycle Bin. + /// A value indicating if the user should confirm the system commands + /// Show a success message after empty Recycle Bin. /// The current theme to use for the icons /// The culture to use for the result's title and sub title - /// A value indicating if the user should confirm the system commands /// A list of all results - internal static List GetSystemCommands(bool isUefi, string iconTheme, CultureInfo culture, bool confirmCommands) + internal static List GetSystemCommands(bool isUefi, bool splitRecycleBinResults, bool confirmCommands, bool emptyRBSuccessMessage, string iconTheme, CultureInfo culture) { var results = new List(); results.AddRange(new[] @@ -106,7 +104,39 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components return ResultHelper.ExecuteCommand(confirmCommands, Resources.Microsoft_plugin_sys_hibernate_confirmation, () => NativeMethods.SetSuspendState(true, true, true)); }, }, - new Result + }); + + // Show Recycle Bin results based on setting. + if (splitRecycleBinResults) + { + results.AddRange(new[] + { + new Result + { + Title = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinOpen", culture), + SubTitle = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_description", culture), + IcoPath = $"Images\\recyclebin.{iconTheme}.png", + Action = c => + { + return Helper.OpenInShell("explorer.exe", "shell:RecycleBinFolder"); + }, + }, + new Result + { + Title = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmptyResult", culture), + SubTitle = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmpty_description", culture), + IcoPath = $"Images\\recyclebin.{iconTheme}.png", + Action = c => + { + ResultHelper.EmptyRecycleBinAsync(emptyRBSuccessMessage); + return true; + }, + }, + }); + } + else + { + results.Add(new Result { Title = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin", culture), SubTitle = Resources.ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_description", culture), @@ -116,8 +146,8 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components { return Helper.OpenInShell("explorer.exe", "shell:RecycleBinFolder"); }, - }, - }); + }); + } // UEFI command/result. It is only available on systems booted in UEFI mode. if (isUefi) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs index cda615809d..c681ae0e03 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components /// /// This class represents the informations for a network connection/interface /// - internal class NetworkConnectionProperties + internal sealed class NetworkConnectionProperties { /// /// Gets the name of the adapter diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs index b769e61796..3886cf7eff 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/ResultHelper.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; -using System.Windows.Navigation; using Microsoft.PowerToys.Run.Plugin.System.Properties; using Wox.Plugin; using Wox.Plugin.Common.Win32; @@ -114,7 +113,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components { executingEmptyRecycleBinTask = true; - // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shemptyrecyclebina/ + // https://learn.microsoft.com/windows/win32/api/shellapi/nf-shellapi-shemptyrecyclebina/ // http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html/ // If the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED)) // If the user canceled the deletion task it will return 2147943623 (0x800704C7 (E_CANCELLED - The operation was canceled by the user.)) @@ -129,7 +128,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components var errorDesc = Win32Helpers.MessageFromHResult((int)result); var name = "Plugin: " + Resources.Microsoft_plugin_sys_plugin_name; var message = $"{Resources.Microsoft_plugin_sys_RecycleBin_ErrorMsg} {errorDesc}"; - Log.Error(message + " - Please refer to https://msdn.microsoft.com/en-us/library/windows/desktop/aa378137 for more information.", typeof(Commands)); + Log.Error(message + " - Please refer to https://msdn.microsoft.com/library/windows/desktop/aa378137 for more information.", typeof(Commands)); _ = MessageBox.Show(message, name, MessageBoxButton.OK, MessageBoxImage.Error); } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs index c6d26c8624..20840e0db3 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/SystemPluginContext.cs @@ -4,7 +4,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Components { - internal class SystemPluginContext + internal sealed class SystemPluginContext { /// /// Gets or sets the type of the result diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs index e6efc47c0a..0fdfe8e0d0 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Main.cs @@ -26,6 +26,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System private bool _showSuccessOnEmptyRB; private bool _localizeSystemCommands; private bool _reduceNetworkResultScore; + private bool _separateEmptyRB; public string Name => Resources.Microsoft_plugin_sys_plugin_name; @@ -56,6 +57,12 @@ namespace Microsoft.PowerToys.Run.Plugin.System Value = true, }, new PluginAdditionalOption() + { + Key = "SeparateResultEmptyRB", + DisplayLabel = Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate, + Value = false, + }, + new PluginAdditionalOption() { Key = "ReduceNetworkResultScore", DisplayLabel = Resources.Reduce_Network_Result_Score, @@ -90,7 +97,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System } // normal system commands are fast and can be returned immediately - var systemCommands = Commands.GetSystemCommands(IsBootedInUefiMode, IconTheme, culture, _confirmSystemCommands); + var systemCommands = Commands.GetSystemCommands(IsBootedInUefiMode, _separateEmptyRB, _confirmSystemCommands, _showSuccessOnEmptyRB, IconTheme, culture); foreach (var c in systemCommands) { var resultMatch = StringMatcher.FuzzySearch(query.Search, c.Title); @@ -209,6 +216,7 @@ namespace Microsoft.PowerToys.Run.Plugin.System var showSuccessOnEmptyRB = false; var localizeSystemCommands = true; var reduceNetworkResultScore = true; + var separateEmptyRB = false; if (settings != null && settings.AdditionalOptions != null) { @@ -223,12 +231,16 @@ namespace Microsoft.PowerToys.Run.Plugin.System var optionNetworkScore = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ReduceNetworkResultScore"); reduceNetworkResultScore = optionNetworkScore?.Value ?? reduceNetworkResultScore; + + var optionSeparateEmptyRB = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "SeparateResultEmptyRB"); + separateEmptyRB = optionSeparateEmptyRB?.Value ?? separateEmptyRB; } _confirmSystemCommands = confirmSystemCommands; _showSuccessOnEmptyRB = showSuccessOnEmptyRB; _localizeSystemCommands = localizeSystemCommands; _reduceNetworkResultScore = reduceNetworkResultScore; + _separateEmptyRB = separateEmptyRB; } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs index fe14b50773..21edf832d4 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.Designer.cs @@ -457,7 +457,16 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Properties { } /// - /// Looks up a localized string similar to Show a success message after empty the Recycle Bin. + /// Looks up a localized string similar to Show separate result for Empty Recycle Bin command. + /// + internal static string Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show a success message after emptying the Recycle Bin. /// internal static string Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage { get { @@ -465,6 +474,33 @@ namespace Microsoft.PowerToys.Run.Plugin.System.Properties { } } + /// + /// Looks up a localized string similar to Empty Recycle Bin. + /// + internal static string Microsoft_plugin_sys_RecycleBinEmpty_description { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmpty_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty Recycle Bin. + /// + internal static string Microsoft_plugin_sys_RecycleBinEmptyResult { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinEmptyResult", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open Recycle Bin. + /// + internal static string Microsoft_plugin_sys_RecycleBinOpen { + get { + return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBinOpen", resourceCulture); + } + } + /// /// Looks up a localized string similar to Restart. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx index 5d6c53aae8..c762257889 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Properties/Resources.resx @@ -244,6 +244,18 @@ Recycle Bin Means the recycle bin folder in Explorer. + + Empty Recycle Bin + This should align to the action in Windows of emptying the recycle bin on your computer. + + + Empty Recycle Bin + This should align to the action in Windows of emptying the recycle bin on your computer. + + + Open Recycle Bin + Means the recycle bin folder in Explorer. + Empty Recycle Bin (Shift+Delete) This should align to the action in Windows of emptying the recycle bin on your computer. @@ -271,6 +283,9 @@ Empty Recycle Bin This should align to the action in Windows of emptying the recycle bin on your computer. + + Show separate result for Empty Recycle Bin command + Show a success message after emptying the Recycle Bin Means the recycle bin folder in Explorer and "emptying" refers to "Empty Recycle Bin" command. diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs index 9910e44388..a33b622d80 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs @@ -60,6 +60,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("iso zone", "ISO 8601 with time zone - ", "Images\\timeDate.dark.png")] [DataRow("iso utc zone", "ISO 8601 UTC with time zone - ", "Images\\timeDate.dark.png")] [DataRow("rfc", "RFC1123 -", "Images\\timeDate.dark.png")] + [DataRow("compatible", "Date and time in filename-compatible format", "Images\\timeDate.dark.png")] public void IconThemeDarkTest(string typedString, string subTitleMatch, string expectedResult) { // Setup @@ -105,6 +106,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("iso zone", "ISO 8601 with time zone - ", "Images\\timeDate.light.png")] [DataRow("iso utc zone", "ISO 8601 UTC with time zone - ", "Images\\timeDate.light.png")] [DataRow("rfc", "RFC1123 -", "Images\\timeDate.light.png")] + [DataRow("compatible", "Date and time in filename-compatible format", "Images\\timeDate.light.png")] public void IconThemeLightTest(string typedString, string subTitleMatch, string expectedResult) { // Setup diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj index 06d1e489ea..e8d5351386 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs index 28aabc2aab..1ebb638df9 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/QueryTests.cs @@ -52,12 +52,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests } [DataTestMethod] - [DataRow("(time", 16)] - [DataRow("(date", 24)] + [DataRow("(time", 17)] + [DataRow("(date", 25)] [DataRow("(year", 7)] - [DataRow("(now", 30)] - [DataRow("(current", 30)] - [DataRow("(", 30)] + [DataRow("(now", 31)] + [DataRow("(current", 31)] + [DataRow("(", 31)] [DataRow("(now::10:10:10", 1)] // Windows file time [DataRow("(current::10:10:10", 0)] public void CountWithPluginKeyword(string typedString, int expectedResultCount) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs index 741a14fe6c..08931e0d57 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs @@ -26,11 +26,16 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests CultureInfo.CurrentUICulture = new CultureInfo("en-us"); } + private DateTime GetDateTimeForTest() + { + return new DateTime(2022, 03, 02, 22, 30, 45); + } + [DataTestMethod] - [DataRow("time", "10:30 AM")] + [DataRow("time", "10:30 PM")] [DataRow("date", "3/2/2022")] - [DataRow("date and time", "3/2/2022 10:30 AM")] - [DataRow("hour", "10")] + [DataRow("date and time", "3/2/2022 10:30 PM")] + [DataRow("hour", "22")] [DataRow("minute", "30")] [DataRow("second", "45")] [DataRow("millisecond", "0")] @@ -45,13 +50,14 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("month and day", "March 2")] [DataRow("year", "2022")] [DataRow("month and year", "March 2022")] - [DataRow("ISO 8601", "2022-03-02T10:30:45")] - [DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")] - [DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")] + [DataRow("ISO 8601", "2022-03-02T22:30:45")] + [DataRow("ISO 8601 with time zone", "2022-03-02T22:30:45")] + [DataRow("RFC1123", "Wed, 02 Mar 2022 22:30:45 GMT")] + [DataRow("Date and time in filename-compatible format", "2022-03-02_22-30-45")] public void LocalFormatsWithShortTimeAndShortDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, false, new DateTime(2022, 03, 02, 10, 30, 45)); + var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest()); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -61,10 +67,10 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests } [DataTestMethod] - [DataRow("time", "10:30 AM")] + [DataRow("time", "10:30 PM")] [DataRow("date", "Wednesday, March 2, 2022")] - [DataRow("date and time", "Wednesday, March 2, 2022 10:30 AM")] - [DataRow("hour", "10")] + [DataRow("date and time", "Wednesday, March 2, 2022 10:30 PM")] + [DataRow("hour", "22")] [DataRow("minute", "30")] [DataRow("second", "45")] [DataRow("millisecond", "0")] @@ -79,13 +85,14 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("month and day", "March 2")] [DataRow("year", "2022")] [DataRow("month and year", "March 2022")] - [DataRow("ISO 8601", "2022-03-02T10:30:45")] - [DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")] - [DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")] + [DataRow("ISO 8601", "2022-03-02T22:30:45")] + [DataRow("ISO 8601 with time zone", "2022-03-02T22:30:45")] + [DataRow("RFC1123", "Wed, 02 Mar 2022 22:30:45 GMT")] + [DataRow("Date and time in filename-compatible format", "2022-03-02_22-30-45")] public void LocalFormatsWithShortTimeAndLongDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, true, new DateTime(2022, 03, 02, 10, 30, 45)); + var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest()); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -95,10 +102,10 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests } [DataTestMethod] - [DataRow("time", "10:30:45 AM")] + [DataRow("time", "10:30:45 PM")] [DataRow("date", "3/2/2022")] - [DataRow("date and time", "3/2/2022 10:30:45 AM")] - [DataRow("hour", "10")] + [DataRow("date and time", "3/2/2022 10:30:45 PM")] + [DataRow("hour", "22")] [DataRow("minute", "30")] [DataRow("second", "45")] [DataRow("millisecond", "0")] @@ -113,13 +120,14 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("month and day", "March 2")] [DataRow("year", "2022")] [DataRow("month and year", "March 2022")] - [DataRow("ISO 8601", "2022-03-02T10:30:45")] - [DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")] - [DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")] + [DataRow("ISO 8601", "2022-03-02T22:30:45")] + [DataRow("ISO 8601 with time zone", "2022-03-02T22:30:45")] + [DataRow("RFC1123", "Wed, 02 Mar 2022 22:30:45 GMT")] + [DataRow("Date and time in filename-compatible format", "2022-03-02_22-30-45")] public void LocalFormatsWithLongTimeAndShortDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, false, new DateTime(2022, 03, 02, 10, 30, 45)); + var helperResults = AvailableResultsList.GetList(true, true, false, GetDateTimeForTest()); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -129,10 +137,10 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests } [DataTestMethod] - [DataRow("time", "10:30:45 AM")] + [DataRow("time", "10:30:45 PM")] [DataRow("date", "Wednesday, March 2, 2022")] - [DataRow("date and time", "Wednesday, March 2, 2022 10:30:45 AM")] - [DataRow("hour", "10")] + [DataRow("date and time", "Wednesday, March 2, 2022 10:30:45 PM")] + [DataRow("hour", "22")] [DataRow("minute", "30")] [DataRow("second", "45")] [DataRow("millisecond", "0")] @@ -147,13 +155,14 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("month and day", "March 2")] [DataRow("year", "2022")] [DataRow("month and year", "March 2022")] - [DataRow("ISO 8601", "2022-03-02T10:30:45")] - [DataRow("ISO 8601 with time zone", "2022-03-02T10:30:45")] - [DataRow("RFC1123", "Wed, 02 Mar 2022 10:30:45 GMT")] + [DataRow("ISO 8601", "2022-03-02T22:30:45")] + [DataRow("ISO 8601 with time zone", "2022-03-02T22:30:45")] + [DataRow("RFC1123", "Wed, 02 Mar 2022 22:30:45 GMT")] + [DataRow("Date and time in filename-compatible format", "2022-03-02_22-30-45")] public void LocalFormatsWithLongTimeAndLongDate(string formatLabel, string expectedResult) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, true, new DateTime(2022, 03, 02, 10, 30, 45)); + var helperResults = AvailableResultsList.GetList(true, true, true, GetDateTimeForTest()); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -168,11 +177,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")] [DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")] [DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")] + [DataRow("Date and time in filename-compatible format", "yyyy-MM-dd_HH-mm-ss")] public void UtcFormatsWithShortTimeAndShortDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, false, new DateTime(2022, 03, 02, 10, 30, 45)); - var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); + var helperResults = AvailableResultsList.GetList(true, false, false, GetDateTimeForTest()); + var expectedResult = GetDateTimeForTest().ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -187,11 +197,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")] [DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")] [DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")] + [DataRow("Date and time in filename-compatible format", "yyyy-MM-dd_HH-mm-ss")] public void UtcFormatsWithShortTimeAndLongDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, false, true, new DateTime(2022, 03, 02, 10, 30, 45)); - var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); + var helperResults = AvailableResultsList.GetList(true, false, true, GetDateTimeForTest()); + var expectedResult = GetDateTimeForTest().ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -206,11 +217,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")] [DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")] [DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")] + [DataRow("Date and time in filename-compatible format", "yyyy-MM-dd_HH-mm-ss")] public void UtcFormatsWithLongTimeAndShortDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, false, new DateTime(2022, 03, 02, 10, 30, 45)); - var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); + var helperResults = AvailableResultsList.GetList(true, true, false, GetDateTimeForTest()); + var expectedResult = GetDateTimeForTest().ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); @@ -225,11 +237,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests [DataRow("ISO 8601 UTC", "yyyy-MM-ddTHH:mm:ss")] [DataRow("ISO 8601 UTC with time zone", "yyyy-MM-ddTHH:mm:ss'Z'")] [DataRow("Universal time format: YYYY-MM-DD hh:mm:ss", "u")] + [DataRow("Date and time in filename-compatible format", "yyyy-MM-dd_HH-mm-ss")] public void UtcFormatsWithLongTimeAndLongDate(string formatLabel, string expectedFormat) { // Setup - var helperResults = AvailableResultsList.GetList(true, true, true, new DateTime(2022, 03, 02, 10, 30, 45)); - var expectedResult = new DateTime(2022, 03, 02, 10, 30, 45).ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); + var helperResults = AvailableResultsList.GetList(true, true, true, GetDateTimeForTest()); + var expectedResult = GetDateTimeForTest().ToUniversalTime().ToString(expectedFormat, CultureInfo.CurrentCulture); // Act var result = helperResults.FirstOrDefault(x => x.Label.Equals(formatLabel, StringComparison.OrdinalIgnoreCase)); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs index 2dfc374485..e051d89568 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs @@ -256,6 +256,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"), IconType = ResultIconType.DateTime, }, + new AvailableResult() + { + Value = dateTimeNow.ToString("yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture), + Label = Resources.Microsoft_plugin_timedate_filename_compatible, + AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"), + IconType = ResultIconType.DateTime, + }, }); } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs index cba39189ed..86e8ca2274 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs @@ -53,7 +53,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components } /// - /// Returns the number week in the month (Used code from 'David Morton' from ) + /// Returns the number week in the month (Used code from 'David Morton' from ) /// /// date /// Number of week in the month diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs index a7871ab040..41c0fc0398 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs @@ -177,6 +177,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties { } } + /// + /// Looks up a localized string similar to Date and time in filename-compatible format. + /// + internal static string Microsoft_plugin_timedate_filename_compatible { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_filename_compatible", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hour. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx index d9555e12c6..c5cd40c0a3 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx @@ -175,6 +175,10 @@ ISO 8601 UTC with time zone 'UTC' means here 'Universal Time Convention' + + Date and time in filename-compatible format + The format allows for embedding in filenames + Millisecond diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj deleted file mode 100644 index 71c7f02728..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - net7.0-windows - false - Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests - - - - - - - - - - - - - - - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/ResultHelperTest.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/ResultHelperTest.cs deleted file mode 100644 index 88b321edc3..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests/ResultHelperTest.cs +++ /dev/null @@ -1,53 +0,0 @@ -// 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. - -using System.Linq; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Helper; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wox.Plugin; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests -{ - [TestClass] - public class ResultHelperTest - { - private TimeZoneList _timeZoneList; - private TimeZoneSettings _timeZoneSettings; - private string _actionKeyword; - - [TestInitialize] - public void SetUp() - { - _actionKeyword = "&"; - _timeZoneList = JsonHelper.ReadAllPossibleTimeZones(); - _timeZoneSettings = new TimeZoneSettings - { - ShowTimeNames = true, - ShowTimeZoneNames = true, - }; - } - - [DataTestMethod] - [DataRow("&MEST", 1)] - [DataRow("&GMT", 1)] - [DataRow("&Germany", 1)] // https://github.com/microsoft/PowerToys/issues/17349 - [DataRow("&AWST", 1)] // https://github.com/microsoft/PowerToys/issues/16695 - [DataRow("&AEDT", 1)] // https://github.com/microsoft/PowerToys/issues/16695 - [DataRow("&AEST", 1)] // https://github.com/microsoft/PowerToys/issues/16695 - public void GetResultsTest(string search, int expectedResultCount) - { - var query = new Query(search, _actionKeyword); - var results = ResultHelper.GetResults(_timeZoneList.TimeZones, _timeZoneSettings, query, string.Empty); - - Assert.AreEqual(expectedResultCount, results.Count()); - - foreach (var result in results) - { - Assert.AreEqual(!result.Title.Contains("UTC"), _timeZoneSettings.ShowTimeZoneNames); - Assert.IsFalse(string.IsNullOrWhiteSpace(result.SubTitle)); - } - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneList.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneList.cs deleted file mode 100644 index 56a78ffe09..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneList.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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. - -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes -{ - /// - /// A class that contains all time zones. - /// - public sealed class TimeZoneList - { - /// - /// Initializes a new instance of the class with empty properties. - /// - /// - /// The standard constructor is need by the -Method. - /// - public TimeZoneList() - { - TimeZones = Enumerable.Empty(); - } - - /// - /// Gets or sets a list with all time zones. - /// - public IEnumerable TimeZones { get; set; } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneProperties.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneProperties.cs deleted file mode 100644 index 80e459bf4d..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneProperties.cs +++ /dev/null @@ -1,96 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes -{ - /// - /// A time zone - /// - public sealed class TimeZoneProperties - { - /// - /// Initializes a new instance of the class with empty properties. - /// - /// - /// The standard constructor is need by the -Method. - /// - public TimeZoneProperties() - { - Offset = "00:00"; - Name = string.Empty; - MilitaryName = string.Empty; - Shortcut = string.Empty; - - TimeNamesStandard = Enumerable.Empty(); - TimeNamesDaylight = Enumerable.Empty(); - CountriesStandard = Enumerable.Empty(); - CountriesDaylight = Enumerable.Empty(); - ShortcutsStandard = Enumerable.Empty(); - ShortcutsDaylight = Enumerable.Empty(); - } - - /// - /// Gets or sets the time offset of this time zone (the gap from the UTC time zone) - /// - public string Offset { get; set; } - - /// - /// Gets or sets the name of this time zone. - /// - public string Name { get; set; } - - /// - /// Gets or sets the military name of this time zone. - /// - public string MilitaryName { get; set; } - - /// - /// Gets or sets the shortcuts of the name this time zone. - /// - public string Shortcut { get; set; } - - /// - /// Gets or sets a list with names for the standard time. - /// - public IEnumerable TimeNamesStandard { get; set; } - - /// - /// Gets or sets a list with names for the daylight saving time. - /// - public IEnumerable TimeNamesDaylight { get; set; } - - /// - /// Gets or sets a list with all countries in this time zone that don't use a daylight saving time. - /// - public IEnumerable CountriesStandard { get; set; } - - /// - /// Gets or sets a list with all countries in this time zone that use a daylight saving time. - /// - public IEnumerable CountriesDaylight { get; set; } - - /// - /// Gets or sets a list with shortcuts for the names for the standard time. - /// - public IEnumerable ShortcutsStandard { get; set; } - - /// - /// Gets or sets a list with shortcuts for the names for the daylight saving time. - /// - public IEnumerable ShortcutsDaylight { get; set; } - - /// - /// Gets a compatible of the . - /// - internal TimeSpan OffsetAsTimeSpan - { - get { return TimeSpan.TryParse(Offset, out var result) ? result : new TimeSpan(0, 0, 0); } - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneSettings.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneSettings.cs deleted file mode 100644 index 8502d165b7..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Classes/TimeZoneSettings.cs +++ /dev/null @@ -1,97 +0,0 @@ -// 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. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties; -using Microsoft.PowerToys.Settings.UI.Library; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes -{ - /// - /// Additional settings for the time zone plugin. - /// - internal sealed class TimeZoneSettings - { - /// - /// Gets or sets a value indicating whether the time zone name of a time zone is shown in the results. - /// - internal bool ShowTimeZoneNames { get; set; } - - /// - /// Gets or sets a value indicating whether the time name of a time zone is shown in the results. - /// - internal bool ShowTimeNames { get; set; } - - /// - /// Gets or sets a value indicating whether the military name of a time zone is shown in the results. - /// - internal bool ShowMilitaryTimeZoneNames { get; set; } - - /// - /// Return a list with all settings. Additional - /// - /// A list with all settings. - internal static List GetAdditionalOptions() - { - var optionList = new List - { - new PluginAdditionalOption - { - Key = "ShowTimeZoneNames", - DisplayLabel = Resources.ShowTimeZoneNames, - Value = true, - }, - new PluginAdditionalOption - { - Key = "ShowTimeNames", - DisplayLabel = Resources.ShowTimeNames, - Value = true, - }, - new PluginAdditionalOption - { - Key = "ShowMilitaryTimeZoneNames", - DisplayLabel = Resources.ShowMilitaryTimeZoneNames, - Value = false, - }, - }; - - return optionList; - } - - /// - /// Update this settings. - /// - /// The settings for all power launcher plugin. - internal void UpdateSettings(PowerLauncherPluginSettings settings) - { - if (settings is null || settings.AdditionalOptions is null) - { - return; - } - - ShowTimeZoneNames = GetSettingOrDefault(settings, "ShowTimeZoneNames"); - ShowTimeNames = GetSettingOrDefault(settings, "ShowTimeNames"); - ShowMilitaryTimeZoneNames = GetSettingOrDefault(settings, "ShowMilitaryTimeZoneNames"); - } - - /// - /// Return one setting of the given settings list with the given name. - /// - /// The object that contain all settings. - /// The name of the setting. - /// A settings value. - private static bool GetSettingOrDefault(PowerLauncherPluginSettings settings, string name) - { - var option = settings.AdditionalOptions.FirstOrDefault(x => x.Key == name); - - // As a fall-back if a setting isn't available, we use the value defined in the method GetAdditionalOptions() - var settingsValue = option?.Value - ?? GetAdditionalOptions().FirstOrDefault(x => x.Key == name)?.Value - ?? default; - - return settingsValue; - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Extensions/StringBuilderExtensions.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Extensions/StringBuilderExtensions.cs deleted file mode 100644 index 0b0fbfb58a..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Extensions/StringBuilderExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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. - -using System.Text; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Extensions -{ - /// - /// Extensions for -Objects - /// - internal static class StringBuilderExtensions - { - /// - /// Save append the given value with the given maximum length to the - /// - /// The to append the string. - /// The value that should be append. - /// The max length of the value that should append. - internal static void SaveAppend(this StringBuilder stringBuilder, string value, int maxLength) - { - if (value.Length > maxLength) - { - stringBuilder.Append(value, 0, maxLength); - } - else - { - stringBuilder.Append(value); - } - } - - /// - /// Cut too long texts to the given length and add three dots at the end of the text. - /// - /// The that contain the text. - /// The maximum length for the text, inclusive the three dots. - internal static void CutTooLong(this StringBuilder stringBuilder, int maxLength) - { - if (stringBuilder.Length <= maxLength) - { - return; - } - - stringBuilder.Length = maxLength - 3; - stringBuilder.Append('.'); - stringBuilder.Append('.'); - stringBuilder.Append('.'); - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/ContextMenuHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/ContextMenuHelper.cs deleted file mode 100644 index 6769ebe534..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/ContextMenuHelper.cs +++ /dev/null @@ -1,71 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Windows; -using System.Windows.Input; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties; -using Wox.Plugin; -using Wox.Plugin.Logger; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper -{ - /// - /// Helper class to easier work with context menu entries - /// - internal static class ContextMenuHelper - { - /// - /// Return a list with all context menu entries for the given - /// Symbols taken from - /// - /// The result for the context menu entires - /// The name of the this assembly - /// A list with context menu entries - internal static List GetContextMenu(in Result result, in string assemblyName) - { - if (!(result?.ContextData is DateTime dateTime)) - { - return new List(0); - } - - var list = new List - { - new ContextMenuResult - { - AcceleratorKey = Key.C, - AcceleratorModifiers = ModifierKeys.Control, - Action = _ => TryToCopyToClipBoard($"{dateTime:HH:mm:ss}"), - FontFamily = "Segoe MDL2 Assets", - Glyph = "\xE8C8", // E8C8 => Symbol: Copy - PluginName = assemblyName, - Title = $"{Resources.CopyTime} (Ctrl+C)", - }, - }; - - return list; - } - - /// - /// Copy the given text to the clipboard - /// - /// The text to copy to the clipboard - /// The text successful copy to the clipboard, otherwise - private static bool TryToCopyToClipBoard(in string text) - { - try - { - Clipboard.Clear(); - Clipboard.SetText(text); - return true; - } - catch (Exception exception) - { - Log.Exception("Can't copy to clipboard", exception, typeof(Main)); - return false; - } - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/JsonHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/JsonHelper.cs deleted file mode 100644 index 2d5d8491a4..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/JsonHelper.cs +++ /dev/null @@ -1,63 +0,0 @@ -// 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. - -using System; -using System.IO; -using System.Reflection; -using System.Text.Json; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes; -using Wox.Plugin.Logger; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper -{ - /// - /// Helper class to easier work with the JSON files. - /// - internal static class JsonHelper - { - /// - /// The name of the file that contains all time zones. - /// - private const string _settingsFile = "timeZones.json"; - - /// - /// Read all possible time zones. - /// - /// A object that contain a list with time zones. - internal static TimeZoneList ReadAllPossibleTimeZones() - { - var assembly = Assembly.GetExecutingAssembly(); - var type = Array.Find(assembly.GetTypes(), x => x.Name == nameof(Main)); - - TimeZoneList? settings = null; - - try - { - var resourceName = $"{type?.Namespace}.{_settingsFile}"; - using var stream = assembly.GetManifestResourceStream(resourceName); - if (stream is null) - { - Log.Error("Stream is null", typeof(JsonHelper)); - return new TimeZoneList(); - } - - using var reader = new StreamReader(stream); - var text = reader.ReadToEnd(); - - settings = JsonSerializer.Deserialize(text); - } - catch (JsonException exception) - { - Log.Exception("Error loading settings JSON file", exception, typeof(JsonHelper)); - } - catch (Exception exception) - { - Log.Exception("Error loading settings JSON file", exception, typeof(JsonHelper)); - throw; - } - - return settings ?? new TimeZoneList(); - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/ResultHelper.cs deleted file mode 100644 index ff4bad8198..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/ResultHelper.cs +++ /dev/null @@ -1,925 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Linq; -using System.Text; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Extensions; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties; -using Wox.Plugin; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper -{ - /// - /// Helper class to easier work with results - /// - internal static class ResultHelper - { - /// - /// Return a list of s based on the given . - /// - /// A list with all possible time zones. - /// Additional options to limit the results. - /// The to filter the . - /// The path to the icon that is used for each result. - /// A list with s. - internal static IEnumerable GetResults(in IEnumerable timeZones, in TimeZoneSettings options, in Query query, in string iconPath) - { - var results = new List(); - var dateTime = DateTime.UtcNow; - - foreach (var timeZone in timeZones) - { - if (MatchTimeZoneShortcut(timeZone, query) - || MatchStandardTimeShortcuts(timeZone, query) - || MatchDaylightTimeShortcuts(timeZone, query) - || MatchTimeZoneNames(timeZone, query) - || MatchStandardTimeNames(timeZone, query) - || MatchDaylightTimeNames(timeZone, query) - || MatchStandardCountries(timeZone, query) - || MatchDaylightCountries(timeZone, query) - || MatchOffset(timeZone, query)) - { - results.AddRange(GetResults(timeZone, options, query, iconPath, dateTime)); - } - } - - var orderResults = results.OrderBy(result => result.Title); - return orderResults; - } - - /// - /// Return a list with based on the given time zone. - /// - /// The time zone that contain the information for the . - /// Additional options to limit the results. - /// The that should match. - /// The path to the icon that is used for each result. - /// The current time in UTC for the . - /// A list with . - internal static IEnumerable GetResults(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings options, in Query query, in string iconPath, in DateTime dateTime) - { - var results = new Collection(); - - var standardTitleResult = GetTitle(timeZoneProperties, options, query, dateTime, false); - var daylightTitleResult = GetTitle(timeZoneProperties, options, query, dateTime, true); - - if (standardTitleResult.Equals(daylightTitleResult)) - { - results.Add(new Result - { - ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, false), - IcoPath = iconPath, - Title = standardTitleResult.ToString(), - SubTitle = GetAllCountries(timeZoneProperties, query, maxLength: 100).ToString(), - ToolTipData = new ToolTipData(standardTitleResult.ToString(), GetAllToolTip(timeZoneProperties, options).ToString()), - }); - - return results; - } - - if (MatchStandardTimeShortcuts(timeZoneProperties, query) - || MatchStandardTimeNames(timeZoneProperties, query) - || MatchStandardCountries(timeZoneProperties, query)) - { - var hasCountries = GetStandardCountries(timeZoneProperties, null, int.MaxValue).Length > 0; - if (!hasCountries) - { - return results; - } - - results.Add(new Result - { - ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, false), - IcoPath = iconPath, - SubTitle = GetStandardCountries(timeZoneProperties, query, maxLength: 100).ToString(), - Title = standardTitleResult.ToString(), - ToolTipData = new ToolTipData(standardTitleResult.ToString(), GetStandardToolTip(timeZoneProperties, options).ToString()), - }); - } - - if (MatchDaylightTimeShortcuts(timeZoneProperties, query) - || MatchDaylightTimeNames(timeZoneProperties, query) - || MatchDaylightCountries(timeZoneProperties, query)) - { - var hasCountries = GetDaylightCountries(timeZoneProperties, null, int.MaxValue).Length > 0; - if (!hasCountries) - { - return results; - } - - results.Add(new Result - { - ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, true), - IcoPath = iconPath, - SubTitle = GetDaylightCountries(timeZoneProperties, query, maxLength: 100).ToString(), - Title = daylightTitleResult.ToString(), - ToolTipData = new ToolTipData(daylightTitleResult.ToString(), GetDaylightToolTip(timeZoneProperties, options).ToString()), - }); - } - - return results; - } - - /// - /// Return the current local time of the given time zone. - /// - /// The time zone that contain all information. - /// The current time in UTC. - /// indicate that the result is for a time zone that use a daylight saving time. - /// The current local time in a time zone. - internal static DateTime GetTimeInTimeZone(in TimeZoneProperties timeZoneProperties, in DateTime dateTime, in bool daylightSavingTime) - { - foreach (var timeZoneInfo in TimeZoneInfo.GetSystemTimeZones()) - { - if (timeZoneInfo.BaseUtcOffset == timeZoneProperties.OffsetAsTimeSpan - && timeZoneInfo.SupportsDaylightSavingTime == daylightSavingTime) - { - return TimeZoneInfo.ConvertTime(dateTime, timeZoneInfo); - } - } - - // Fall-back - var result = dateTime + timeZoneProperties.OffsetAsTimeSpan; - return result; - } - - /// - /// Return the title for the given time zone. - /// - /// The time zone that contain all information. - /// Additional options to limit the results. - /// The that should match. - /// The current time in UTC. - /// indicate that the result is for a time zone that use a daylight saving time. - /// A title for a time zone. - internal static StringBuilder GetTitle(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings, in Query query, in DateTime dateTime, in bool daylightSavingTime) - { - var stringBuilder = new StringBuilder(); - - var timeInZoneTime = GetTimeInTimeZone(timeZoneProperties, dateTime, daylightSavingTime); - var timeZoneNames = GetNames(timeZoneProperties, timeZoneSettings, query, maxLength: 50); - - stringBuilder.AppendFormat(CultureInfo.CurrentCulture, "{0:HH:mm:ss}", timeInZoneTime); - stringBuilder.Append(' '); - stringBuilder.Append('-'); - stringBuilder.Append(' '); - stringBuilder.Append(timeZoneNames); - - return stringBuilder; - } - - /// - /// Return a tool-tip for the given time zone with countries that use the standard time. - /// - /// The time zone that contain all information. - /// Additional options to limit the results. - /// A tool-tip with countries that use the standard time. - internal static StringBuilder GetStandardToolTip(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings) - { - var countries = GetStandardCountries(timeZoneProperties, null, maxLength: int.MaxValue); - var names = GetNames(timeZoneProperties, timeZoneSettings, null, maxLength: int.MaxValue); - var shortcuts = GetStandardShortcuts(timeZoneProperties); - - if (!string.IsNullOrWhiteSpace(timeZoneProperties.Shortcut)) - { - shortcuts.Append(','); - shortcuts.Append(' '); - shortcuts.Append(timeZoneProperties.Shortcut); - } - - var stringBuilder = new StringBuilder(); - - stringBuilder.Append(Resources.Offset).Append(':').Append(' ').AppendLine(timeZoneProperties.Offset); - stringBuilder.Append(Resources.UseDst).Append(':').Append(' ').AppendLine(Resources.No); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Names).Append(':').Append(' ').Append(names); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Shortcuts).Append(':').Append(' ').Append(shortcuts); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Countries).Append(':').Append(' ').Append(countries); - - return stringBuilder; - } - - /// - /// Return a tool-tip for the given time zone with countries that use the daylight saving time. - /// - /// The time zone that contain all information. - /// Additional options to limit the type of the names. - /// A tool-tip with countries that use the daylight saving time. - internal static StringBuilder GetDaylightToolTip(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings) - { - var dstCountries = GetDaylightCountries(timeZoneProperties, null, maxLength: int.MaxValue); - var names = GetNames(timeZoneProperties, timeZoneSettings, null, maxLength: int.MaxValue); - var shortcuts = GetDaylightShortcuts(timeZoneProperties); - - if (!string.IsNullOrWhiteSpace(timeZoneProperties.Shortcut)) - { - shortcuts.Append(','); - shortcuts.Append(' '); - shortcuts.Append(timeZoneProperties.Shortcut); - } - - var stringBuilder = new StringBuilder(); - - stringBuilder.Append(Resources.Offset).Append(':').Append(' ').AppendLine(timeZoneProperties.Offset); - stringBuilder.Append(Resources.UseDst).Append(':').Append(' ').AppendLine(Resources.Yes); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Names).Append(':').Append(' ').Append(names); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Shortcuts).Append(':').Append(' ').Append(shortcuts); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.CountriesWithDst).Append(':').Append(' ').Append(dstCountries); - - return stringBuilder; - } - - /// - /// Return a tool-tip for the given time zone with countries. - /// - /// The time zone that contain all information. - /// Additional options to limit the type of the names. - /// A tool-tip with countries. - internal static StringBuilder GetAllToolTip(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings) - { - var countries = GetStandardCountries(timeZoneProperties, null, maxLength: int.MaxValue); - var dstCountries = GetDaylightCountries(timeZoneProperties, null, maxLength: int.MaxValue); - var names = GetNames(timeZoneProperties, timeZoneSettings, null, maxLength: int.MaxValue); - var shortcuts = GetStandardShortcuts(timeZoneProperties); - var dstShortcuts = GetDaylightShortcuts(timeZoneProperties); - - if (dstShortcuts.Length > 0) - { - shortcuts.Append(','); - shortcuts.Append(' '); - shortcuts.Append(dstShortcuts); - } - - if (!string.IsNullOrWhiteSpace(timeZoneProperties.Shortcut)) - { - shortcuts.Append(','); - shortcuts.Append(' '); - shortcuts.Append(timeZoneProperties.Shortcut); - } - - var stringBuilder = new StringBuilder(); - - stringBuilder.Append(Resources.Offset).Append(':').Append(' ').AppendLine(timeZoneProperties.Offset); - stringBuilder.Append(Resources.UseDst).Append(':').Append(' ').AppendLine(Resources.Yes); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Names).Append(':').Append(' ').Append(names); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Shortcuts).Append(':').Append(' ').Append(shortcuts); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.Countries).Append(':').Append(' ').Append(countries); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine(string.Empty); - stringBuilder.Append(Resources.CountriesWithDst).Append(':').Append(' ').Append(dstCountries); - - return stringBuilder; - } - - /// - /// Return all names of the given time zone that match the given query. - /// - /// The time zone that contain a hand of names. - /// Additional options to limit the type of the names. - /// The query that should match. - /// The maximum length of the result. - /// All know names of the given time zone. - internal static StringBuilder GetNames(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings, Query? query, in int maxLength) - { - var allNames = new List(); - - if (!string.IsNullOrWhiteSpace(timeZoneProperties.Name) && timeZoneSettings.ShowTimeZoneNames) - { - allNames.Add(timeZoneProperties.Name); - } - - if (!string.IsNullOrWhiteSpace(timeZoneProperties.MilitaryName) && timeZoneSettings.ShowMilitaryTimeZoneNames) - { - allNames.Add(timeZoneProperties.MilitaryName); - } - - if (timeZoneProperties.TimeNamesStandard != null && timeZoneSettings.ShowTimeZoneNames) - { - allNames.AddRange(timeZoneProperties.TimeNamesStandard); - } - - if (timeZoneProperties.TimeNamesDaylight != null && timeZoneSettings.ShowTimeZoneNames) - { - allNames.AddRange(timeZoneProperties.TimeNamesDaylight); - } - - IEnumerable names; - - if (query is null || string.IsNullOrWhiteSpace(query.Search)) - { - names = allNames; - } - else if (MatchStandardCountries(timeZoneProperties, query) || MatchDaylightCountries(timeZoneProperties, query)) - { - names = allNames; - } - else if (MatchStandardTimeShortcuts(timeZoneProperties, query) || MatchDaylightTimeShortcuts(timeZoneProperties, query)) - { - var matches = new Collection(); - - foreach (var name in allNames) - { - var matchAll = query.Search.All(x => name.Contains(x, StringComparison.CurrentCultureIgnoreCase)); - if (matchAll) - { - matches.Add(name); - } - } - - names = matches; - } - else - { - names = allNames.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - } - - var stringBuilder = new StringBuilder(); - - if (names.Any()) - { - var lastEntry = names.LastOrDefault(); - - foreach (var name in names) - { - stringBuilder.Append(name); - - if (name != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - - // To many names (first pass) => use shortcuts - if (stringBuilder.Length > maxLength) - { - stringBuilder.Replace(Resources.TimeZone, Resources.TimeZoneShortcut); - stringBuilder.Replace(Resources.StandardTime, Resources.StandardTimeShortcut); - stringBuilder.Replace(Resources.DaylightTime, Resources.DaylightTimeShortcut); - stringBuilder.Replace(Resources.Time, Resources.TimeShortcut); - } - - // To many names (second pass) => cut name length - if (stringBuilder.Length > maxLength) - { - foreach (var country in names) - { - stringBuilder.SaveAppend(country, maxLength: 5); - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - stringBuilder.CutTooLong(maxLength); - } - else - { - // only when we don't have found any names so we - stringBuilder.Append("UTC"); - - var totalMinutes = timeZoneProperties.OffsetAsTimeSpan.TotalMinutes; - if (totalMinutes < 0) - { - stringBuilder.Append(timeZoneProperties.Offset); - } - else if (totalMinutes > 0) - { - stringBuilder.Append('+'); - stringBuilder.Append(timeZoneProperties.Offset); - } - else - { - stringBuilder.Append("±00:00"); - } - } - - return stringBuilder; - } - - /// - /// Return all standard time name shortcuts of the given time zone. - /// - /// The time zone that contain a hand of names. - /// All standard time name shortcuts of the given time zone. - internal static StringBuilder GetStandardShortcuts(in TimeZoneProperties timeZoneProperties) - { - var stringBuilder = new StringBuilder(); - var lastEntry = timeZoneProperties.ShortcutsStandard.LastOrDefault(); - - foreach (var name in timeZoneProperties.ShortcutsStandard) - { - stringBuilder.Append(name); - - if (name != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - - return stringBuilder; - } - - /// - /// Return all know daylight time name shortcuts of the given time zone. - /// - /// The time zone that contain a hand of names. - /// All know daylight time name shortcuts of the given time zone. - internal static StringBuilder GetDaylightShortcuts(in TimeZoneProperties timeZoneProperties) - { - var stringBuilder = new StringBuilder(); - var lastEntry = timeZoneProperties.ShortcutsDaylight.LastOrDefault(); - - foreach (var name in timeZoneProperties.ShortcutsDaylight) - { - stringBuilder.Append(name); - - if (name != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - - return stringBuilder; - } - - /// - /// Return all countries that use the standard time of the given time zone that match the given query. - /// - /// The time zone that contain the countries. - /// The that should match a country that use standard time. - /// The maximum length of the result. - /// All countries that use the standard time of the given time zone. - internal static StringBuilder GetStandardCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength) - { - IEnumerable countries; - - if (query is null || string.IsNullOrWhiteSpace(query.Search)) - { - countries = timeZoneProperties.CountriesStandard; - } - else if (MatchStandardTimeShortcuts(timeZoneProperties, query)) - { - var matches = new Collection(); - - foreach (var name in timeZoneProperties.CountriesStandard) - { - var matchAll = query.Search.All(x => name.Contains(x, StringComparison.CurrentCultureIgnoreCase)); - if (matchAll) - { - matches.Add(name); - } - } - - countries = matches; - } - else - { - countries = timeZoneProperties.CountriesStandard.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - } - - // When the search query don't match a country, show all countries - if (countries is null || !countries.Any()) - { - countries = timeZoneProperties.CountriesStandard; - } - - var stringBuilder = new StringBuilder(); - var lastEntry = countries.LastOrDefault(); - - foreach (var country in countries.Distinct()) - { - stringBuilder.Append(country); - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - - // To many countries (first pass) => remove extra info - if (stringBuilder.Length > maxLength) - { - stringBuilder.Clear(); - - foreach (var country in countries) - { - var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase); - if (extraInfoStart > 0) - { - stringBuilder.Append(country[..extraInfoStart]); - } - else - { - stringBuilder.Append(country); - } - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - // To many countries (second pass) => remove extra info and cut country length - if (stringBuilder.Length > maxLength) - { - foreach (var country in countries) - { - var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase); - if (extraInfoStart > 0) - { - stringBuilder.SaveAppend(country[..extraInfoStart], maxLength: 5); - } - else - { - stringBuilder.SaveAppend(country, maxLength: 5); - } - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - stringBuilder.CutTooLong(maxLength); - - return stringBuilder; - } - - /// - /// Return all countries that use the daylight saving time of the given time zone that match the given query - /// - /// The time zone that contain the countries. - /// The that should match a country that use daylight time. - /// The maximum length of the result. - /// All countries that use the daylight saving time of the given time zone. - internal static StringBuilder GetDaylightCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength) - { - IEnumerable countries; - - if (query is null || string.IsNullOrWhiteSpace(query.Search)) - { - countries = timeZoneProperties.CountriesDaylight; - } - else if (MatchDaylightTimeShortcuts(timeZoneProperties, query)) - { - var matches = new Collection(); - - foreach (var name in timeZoneProperties.CountriesDaylight) - { - var matchAll = query.Search.All(x => name.Contains(x, StringComparison.CurrentCultureIgnoreCase)); - if (matchAll) - { - matches.Add(name); - } - } - - countries = matches; - } - else - { - countries = timeZoneProperties.CountriesDaylight.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - } - - // When the search query don't match a country, show all countries - if (countries is null || !countries.Any()) - { - countries = timeZoneProperties.CountriesDaylight; - } - - var stringBuilder = new StringBuilder(); - var lastEntry = countries.LastOrDefault(); - - foreach (var country in countries.Distinct()) - { - stringBuilder.Append(country); - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - - // To many countries (first pass) => remove extra info - if (stringBuilder.Length > maxLength) - { - stringBuilder.Clear(); - - foreach (var country in countries) - { - var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase); - if (extraInfoStart > 0) - { - stringBuilder.Append(country[..extraInfoStart]); - } - else - { - stringBuilder.Append(country); - } - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - // To many countries (second pass) => remove extra info and cut country length - if (stringBuilder.Length > maxLength) - { - foreach (var country in countries) - { - var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase); - if (extraInfoStart > 0) - { - stringBuilder.SaveAppend(country[..extraInfoStart], maxLength: 5); - } - else - { - stringBuilder.SaveAppend(country, maxLength: 5); - } - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - stringBuilder.CutTooLong(maxLength); - - return stringBuilder; - } - - /// - /// Return all countries of the given time zone that match the given query. - /// - /// The time zone that contain the countries. - /// The that should match a country that use standard or daylight time. - /// The maximum length of the result. - /// All countries of the given time zone. - internal static StringBuilder GetAllCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength) - { - IEnumerable countries; - - if (query is null || string.IsNullOrWhiteSpace(query.Search)) - { - countries = timeZoneProperties.CountriesDaylight - .Concat(timeZoneProperties.CountriesStandard); - } - else if (MatchDaylightTimeShortcuts(timeZoneProperties, query) || MatchStandardTimeShortcuts(timeZoneProperties, query)) - { - var matches = new Collection(); - - foreach (var name in timeZoneProperties.CountriesDaylight.Concat(timeZoneProperties.CountriesStandard)) - { - var matchAll = query.Search.All(x => name.Contains(x, StringComparison.CurrentCultureIgnoreCase)); - if (matchAll) - { - matches.Add(name); - } - } - - countries = matches; - } - else - { - countries = timeZoneProperties.CountriesDaylight.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) - .Concat(timeZoneProperties.CountriesStandard.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase))); - } - - // When the search query don't match a country, show all countries - if (countries is null || !countries.Any()) - { - countries = timeZoneProperties.CountriesDaylight - .Concat(timeZoneProperties.CountriesStandard); - } - - var stringBuilder = new StringBuilder(); - var lastEntry = countries.LastOrDefault(); - - foreach (var country in countries.Distinct()) - { - stringBuilder.Append(country); - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - - // To many countries (first pass) => remove extra info - if (stringBuilder.Length > maxLength) - { - stringBuilder.Clear(); - - foreach (var country in countries) - { - var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase); - if (extraInfoStart > 0) - { - stringBuilder.Append(country[..extraInfoStart]); - } - else - { - stringBuilder.Append(country); - } - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - // To many countries (second pass) => remove extra info and cut country length - if (stringBuilder.Length > maxLength) - { - foreach (var country in countries) - { - var extraInfoStart = country.IndexOf('(', StringComparison.InvariantCultureIgnoreCase); - if (extraInfoStart > 0) - { - stringBuilder.SaveAppend(country[..extraInfoStart], maxLength: 5); - } - else - { - stringBuilder.SaveAppend(country, maxLength: 5); - } - - if (country != lastEntry) - { - stringBuilder.Append(','); - stringBuilder.Append(' '); - } - } - } - - stringBuilder.CutTooLong(maxLength); - - return stringBuilder; - } - - /// - /// Indicate that the given query match the time zone shortcut of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchTimeZoneShortcut(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.Shortcut.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase); - return result; - } - - /// - /// Indicate that the given query match one of the time zone names of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchTimeZoneNames(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.Name.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase) - || timeZoneProperties.MilitaryName.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase); - - return result; - } - - /// - /// Indicate that the given query match the offset of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchOffset(in TimeZoneProperties timeZoneProperties, Query query) - { - // allow search for "-xx:xx" - if (timeZoneProperties.Offset.StartsWith('-') && query.Search.StartsWith('-')) - { - if (timeZoneProperties.Offset[1..].Contains(query.Search[1..], StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - - // allow search for "+xx:xx" - if (!timeZoneProperties.Offset.StartsWith('-') && query.Search.StartsWith('+')) - { - if (timeZoneProperties.Offset.Contains(query.Search[1..], StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - - return false; - } - - /// - /// Indicate that the given query match one of the standard time names of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchStandardTimeNames(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.TimeNamesStandard.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - return result; - } - - /// - /// Indicate that the given query match one of the daylight time names of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchDaylightTimeNames(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.TimeNamesDaylight.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - return result; - } - - /// - /// Indicate that the given query match one of the countries that use the standard time of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchStandardCountries(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.CountriesStandard.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - return result; - } - - /// - /// Indicate that the given query match one of the countries that use the daylight time of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchDaylightCountries(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.CountriesDaylight.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - return result; - } - - /// - /// Indicate that the given query match the time zone shortcut of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchStandardTimeShortcuts(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.ShortcutsStandard.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - return result; - } - - /// - /// Indicate that the given query match the time zone shortcut of the given time zone. - /// - /// The time zone to check. - /// The query that should match. - /// if the query match, otherwise . - internal static bool MatchDaylightTimeShortcuts(in TimeZoneProperties timeZoneProperties, Query query) - { - var result = timeZoneProperties.ShortcutsDaylight.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)); - return result; - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/TranslationHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/TranslationHelper.cs deleted file mode 100644 index fa46d4ee97..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Helper/TranslationHelper.cs +++ /dev/null @@ -1,133 +0,0 @@ -// 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. - -using System.Collections.Generic; -using System.Globalization; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties; -using Wox.Plugin.Logger; - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper -{ - /// - /// Helper class to easier work with translations. - /// - internal static class TranslationHelper - { - /// - /// Translate all names and countries of the class. - /// - /// A class that contain all possible time zones. - internal static void TranslateAllSettings(in TimeZoneList timeZoneList) - { - if (timeZoneList?.TimeZones is null) - { - return; - } - - foreach (var timeZone in timeZoneList.TimeZones) - { - // Translate Name - if (!string.IsNullOrWhiteSpace(timeZone.Name)) - { - var name = Resources.ResourceManager.GetString(timeZone.Name, CultureInfo.InvariantCulture); - if (string.IsNullOrEmpty(name)) - { - Log.Warn($"Resource string for [{timeZone.Name}] not found", typeof(TranslationHelper)); - } - - timeZone.Name = name ?? timeZone.Name ?? string.Empty; - } - - // Translate MilitaryName - if (!string.IsNullOrWhiteSpace(timeZone.MilitaryName)) - { - var militaryName = Resources.ResourceManager.GetString(timeZone.MilitaryName, CultureInfo.InvariantCulture); - if (string.IsNullOrEmpty(militaryName)) - { - Log.Warn($"Resource string for [{timeZone.MilitaryName}] not found", typeof(TranslationHelper)); - } - - timeZone.MilitaryName = militaryName ?? timeZone.MilitaryName ?? string.Empty; - } - - // Translate TimeNamesDaylight - if (!(timeZone.TimeNamesDaylight is null)) - { - var timeNamesDaylight = new List(); - - foreach (var nameDaylight in timeZone.TimeNamesDaylight) - { - var nameDaylightT = Resources.ResourceManager.GetString(nameDaylight, CultureInfo.InvariantCulture); - if (string.IsNullOrEmpty(nameDaylightT)) - { - Log.Warn($"Resource string for [{nameDaylight}] not found", typeof(TranslationHelper)); - } - - timeNamesDaylight.Add(nameDaylightT ?? nameDaylight ?? string.Empty); - } - - timeZone.TimeNamesDaylight = timeNamesDaylight; - } - - // Translate TimeNamesStandard - if (!(timeZone.TimeNamesStandard is null)) - { - var timeNamesStandard = new List(); - - foreach (var nameStandard in timeZone.TimeNamesStandard) - { - var nameStandardT = Resources.ResourceManager.GetString(nameStandard, CultureInfo.InvariantCulture); - if (string.IsNullOrEmpty(nameStandardT)) - { - Log.Warn($"Resource string for [{nameStandard}] not found", typeof(TranslationHelper)); - } - - timeNamesStandard.Add(nameStandardT ?? nameStandard ?? string.Empty); - } - - timeZone.TimeNamesStandard = timeNamesStandard; - } - - // Translate CountriesDaylight - if (!(timeZone.CountriesDaylight is null)) - { - var countriesDaylight = new List(); - - foreach (var countryDaylight in timeZone.CountriesDaylight) - { - var countryDaylightT = Resources.ResourceManager.GetString(countryDaylight, CultureInfo.InvariantCulture); - if (string.IsNullOrEmpty(countryDaylightT)) - { - Log.Warn($"Resource string for [{countryDaylight}] not found", typeof(TranslationHelper)); - } - - countriesDaylight.Add(countryDaylightT ?? countryDaylight ?? string.Empty); - } - - timeZone.CountriesDaylight = countriesDaylight; - } - - // Translate CountriesStandard - if (!(timeZone.CountriesStandard is null)) - { - var countriesStandard = new List(); - - foreach (var countryStandard in timeZone.CountriesStandard) - { - var countryStandardT = Resources.ResourceManager.GetString(countryStandard, CultureInfo.InvariantCulture); - if (string.IsNullOrEmpty(countryStandardT)) - { - Log.Warn($"Resource string for [{countryStandard}] not found", typeof(TranslationHelper)); - } - - countriesStandard.Add(countryStandardT ?? countryStandard ?? string.Empty); - } - - timeZone.CountriesStandard = countriesStandard; - } - } - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Images/timeZone.dark.png b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Images/timeZone.dark.png deleted file mode 100644 index 5e523a5d46..0000000000 Binary files a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Images/timeZone.dark.png and /dev/null differ diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Images/timeZone.light.png b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Images/timeZone.light.png deleted file mode 100644 index 0dddc99205..0000000000 Binary files a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Images/timeZone.light.png and /dev/null differ diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Main.cs deleted file mode 100644 index 8e191b9084..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Main.cs +++ /dev/null @@ -1,220 +0,0 @@ -// 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. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Windows.Controls; -using ManagedCommon; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Helper; -using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties; -using Microsoft.PowerToys.Settings.UI.Library; -using Wox.Plugin; - -[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeZone.UnitTests")] - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone -{ - /// - /// A power launcher plugin to search across time zones. - /// - public class Main : IPlugin, IContextMenu, IPluginI18n, ISettingProvider, IDisposable - { - /// - /// The name of this assembly - /// - private readonly string _assemblyName; - - /// - /// The settings for this plugin. - /// - private readonly TimeZoneSettings _timeZoneSettings; - - /// - /// The initial context for this plugin (contains API and meta-data) - /// - private PluginInitContext? _context; - - /// - /// The path to the icon for each result - /// - private string _defaultIconPath; - - /// - /// Indicate that the plugin is disposed - /// - private bool _disposed; - - /// - /// A class that contain all possible time zones. - /// - private TimeZoneList? _timeZoneList; - - /// - /// Initializes a new instance of the class. - /// - public Main() - { - _assemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? GetTranslatedPluginTitle(); - _defaultIconPath = "Images/timeZone.light.png"; - _timeZoneSettings = new TimeZoneSettings(); - } - - /// - /// Gets the localized name. - /// - public string Name - { - get { return Resources.PluginTitle; } - } - - /// - /// Gets the localized description. - /// - public string Description - { - get { return Resources.PluginDescription; } - } - - /// - /// Gets the additional options for this plugin. - /// - public IEnumerable AdditionalOptions - { - get { return TimeZoneSettings.GetAdditionalOptions(); } - } - - /// - /// Initialize the plugin with the given - /// - /// The for this plugin - public void Init(PluginInitContext context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - _context.API.ThemeChanged += OnThemeChanged; - UpdateIconPath(_context.API.GetCurrentTheme()); - - _timeZoneList = JsonHelper.ReadAllPossibleTimeZones(); - - TranslationHelper.TranslateAllSettings(_timeZoneList); - } - - /// - /// Return a filtered list, based on the given query - /// - /// The query to filter the list - /// A filtered list, can be empty when nothing was found - public List Query(Query query) - { - if (_timeZoneList?.TimeZones is null) - { - return new List(0); - } - - if (query is null) - { - return new List(0); - } - - var results = ResultHelper.GetResults(_timeZoneList.TimeZones, _timeZoneSettings, query, _defaultIconPath); - return results.ToList(); - } - - /// - /// Return a list context menu entries for a given (shown at the right side of the result) - /// - /// The for the list with context menu entries - /// A list context menu entries - public List LoadContextMenus(Result selectedResult) - { - return ContextMenuHelper.GetContextMenu(selectedResult, _assemblyName); - } - - /// - /// Change all theme-based elements (typical called when the plugin theme has changed) - /// - /// The old - /// The new - private void OnThemeChanged(Theme oldtheme, Theme newTheme) - { - UpdateIconPath(newTheme); - } - - /// - /// Update all icons (typical called when the plugin theme has changed) - /// - /// The new for the icons - private void UpdateIconPath(Theme theme) - { - _defaultIconPath = theme == Theme.Light || theme == Theme.HighContrastWhite - ? "Images/timeZone.light.png" - : "Images/timeZone.dark.png"; - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Wrapper method for that dispose additional objects and events form the plugin itself - /// - /// Indicate that the plugin is disposed - protected virtual void Dispose(bool disposing) - { - if (_disposed || !disposing) - { - return; - } - - if (!(_context is null)) - { - _context.API.ThemeChanged -= OnThemeChanged; - } - - _disposed = true; - } - - /// - /// Return the translated plugin title. - /// - /// A translated plugin title. - public string GetTranslatedPluginTitle() - { - return Resources.PluginTitle; - } - - /// - /// Return the translated plugin description. - /// - /// A translated plugin description. - public string GetTranslatedPluginDescription() - { - return Resources.PluginDescription; - } - - /// - /// Return a additional setting panel for this plugin. - /// - /// A additional setting panel. - public Control CreateSettingPanel() - { - throw new NotImplementedException(); - } - - /// - /// Update the plugin settings - /// - /// The settings for all power launcher plugin. - public void UpdateSettings(PowerLauncherPluginSettings settings) - { - _timeZoneSettings.UpdateSettings(settings); - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Microsoft.PowerToys.Run.Plugin.TimeZone.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Microsoft.PowerToys.Run.Plugin.TimeZone.csproj deleted file mode 100644 index ccb8af0494..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Microsoft.PowerToys.Run.Plugin.TimeZone.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - net7.0-windows - Microsoft.PowerToys.Run.Plugin.TimeZone - Microsoft.PowerToys.Run.Plugin.TimeZone - $(Version).0 - false - false - enable - true - ..\..\..\..\..\$(Platform)\$(Configuration)\modules\launcher\Plugins\TimeZone\ - - - - true - DEBUG;TRACE - full - false - - - - TRACE - true - pdbonly - - - - - - - - - false - - - false - - - - - - PreserveNewest - - - - - - True - True - Resources.resx - - - True - True - Resources.resx - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - PreserveNewest - - - PreserveNewest - - - - - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs deleted file mode 100644 index 78735dc479..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.Designer.cs +++ /dev/null @@ -1,6651 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.PowerToys.Run.Plugin.TimeZone.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Abkhazia. - /// - internal static string Abkhazia { - get { - return ResourceManager.GetString("Abkhazia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Acre (Brazil). - /// - internal static string Acre__Brazil_ { - get { - return ResourceManager.GetString("Acre (Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Acre Time. - /// - internal static string Acre_Time { - get { - return ResourceManager.GetString("Acre Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Afghanistan. - /// - internal static string Afghanistan { - get { - return ResourceManager.GetString("Afghanistan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Afghanistan Time. - /// - internal static string Afghanistan_Time { - get { - return ResourceManager.GetString("Afghanistan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Akrotiri and Dhekelia. - /// - internal static string Akrotiri_and_Dhekelia { - get { - return ResourceManager.GetString("Akrotiri and Dhekelia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Aktobe (Kazakhstan). - /// - internal static string Aktobe__Kazakhstan_ { - get { - return ResourceManager.GetString("Aktobe (Kazakhstan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alabama (United States). - /// - internal static string Alabama__United_States_ { - get { - return ResourceManager.GetString("Alabama (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alaska (most, United States). - /// - internal static string Alaska__most__United_States_ { - get { - return ResourceManager.GetString("Alaska (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alaska Daylight Time. - /// - internal static string Alaska_Daylight_Time { - get { - return ResourceManager.GetString("Alaska Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alaska Standard Time. - /// - internal static string Alaska_Standard_Time { - get { - return ResourceManager.GetString("Alaska Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alaska Time Zone. - /// - internal static string Alaska_Time_Zone { - get { - return ResourceManager.GetString("Alaska Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Albania. - /// - internal static string Albania { - get { - return ResourceManager.GetString("Albania", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alberta (Canada). - /// - internal static string Alberta__Canada_ { - get { - return ResourceManager.GetString("Alberta (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Algeria. - /// - internal static string Algeria { - get { - return ResourceManager.GetString("Algeria", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alma-Ata Time. - /// - internal static string Alma_Ata_Time { - get { - return ResourceManager.GetString("Alma-Ata Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Alpha Time Zone. - /// - internal static string Alpha_Time_Zone { - get { - return ResourceManager.GetString("Alpha Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Altai Krai (Russia). - /// - internal static string Altai_Krai__Russia_ { - get { - return ResourceManager.GetString("Altai Krai (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Altai Republic (Russia). - /// - internal static string Altai_Republic__Russia_ { - get { - return ResourceManager.GetString("Altai Republic (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Amazon Summer Time. - /// - internal static string Amazon_Summer_Time { - get { - return ResourceManager.GetString("Amazon Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Amazon Time. - /// - internal static string Amazon_Time { - get { - return ResourceManager.GetString("Amazon Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Amazonas (most, Brazil). - /// - internal static string Amazonas__most__Brazil_ { - get { - return ResourceManager.GetString("Amazonas (most, Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to American Samoa. - /// - internal static string American_Samoa { - get { - return ResourceManager.GetString("American Samoa", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Amsterdam Island (French Southern and Antarctic Lands). - /// - internal static string Amsterdam_Island__French_Southern_and_Antarctic_Lands_ { - get { - return ResourceManager.GetString("Amsterdam Island (French Southern and Antarctic Lands)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Amur (Russia). - /// - internal static string Amur__Russia_ { - get { - return ResourceManager.GetString("Amur (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Anadyr Time. - /// - internal static string Anadyr_Time { - get { - return ResourceManager.GetString("Anadyr Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Andorra. - /// - internal static string Andorra { - get { - return ResourceManager.GetString("Andorra", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Andreanof Islands (United States). - /// - internal static string Andreanof_Islands__United_States_ { - get { - return ResourceManager.GetString("Andreanof Islands (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Angola. - /// - internal static string Angola { - get { - return ResourceManager.GetString("Angola", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Anguilla. - /// - internal static string Anguilla { - get { - return ResourceManager.GetString("Anguilla", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Antigua and Barbuda. - /// - internal static string Antigua_and_Barbuda { - get { - return ResourceManager.GetString("Antigua and Barbuda", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Aqtobe Time. - /// - internal static string Aqtobe_Time { - get { - return ResourceManager.GetString("Aqtobe Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Arabia Standard Time. - /// - internal static string Arabia_Standard_Time { - get { - return ResourceManager.GetString("Arabia Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Argentina. - /// - internal static string Argentina { - get { - return ResourceManager.GetString("Argentina", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Argentina Time. - /// - internal static string Argentina_Time { - get { - return ResourceManager.GetString("Argentina Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Arizona (most, United States). - /// - internal static string Arizona__most__United_States_ { - get { - return ResourceManager.GetString("Arizona (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Arkansas (United States). - /// - internal static string Arkansas__United_States_ { - get { - return ResourceManager.GetString("Arkansas (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Armenia. - /// - internal static string Armenia { - get { - return ResourceManager.GetString("Armenia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Armenia Time. - /// - internal static string Armenia_Time { - get { - return ResourceManager.GetString("Armenia Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Artsakh. - /// - internal static string Artsakh { - get { - return ResourceManager.GetString("Artsakh", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Aruba. - /// - internal static string Aruba { - get { - return ResourceManager.GetString("Aruba", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ascension and Tristan da Cunha. - /// - internal static string Ascension_and_Tristan_da_Cunha { - get { - return ResourceManager.GetString("Ascension and Tristan da Cunha", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ASEAN Common Time. - /// - internal static string ASEAN_Common_Time { - get { - return ResourceManager.GetString("ASEAN Common Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Astrakhan (Russia). - /// - internal static string Astrakhan__Russia_ { - get { - return ResourceManager.GetString("Astrakhan (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Atikokan (Canada). - /// - internal static string Atikokan__Canada_ { - get { - return ResourceManager.GetString("Atikokan (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Atlantic Daylight Time. - /// - internal static string Atlantic_Daylight_Time { - get { - return ResourceManager.GetString("Atlantic Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Atlantic Standard Time. - /// - internal static string Atlantic_Standard_Time { - get { - return ResourceManager.GetString("Atlantic Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Atlantic Time Zone. - /// - internal static string Atlantic_Time_Zone { - get { - return ResourceManager.GetString("Atlantic Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Atyrau (Kazakhstan). - /// - internal static string Atyrau__Kazakhstan_ { - get { - return ResourceManager.GetString("Atyrau (Kazakhstan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australia: Western Australia (most). - /// - internal static string Australia__Western_Australia__most_ { - get { - return ResourceManager.GetString("Australia: Western Australia (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Capital Territory (Australia). - /// - internal static string Australian_Capital_Territory__Australia_ { - get { - return ResourceManager.GetString("Australian Capital Territory (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Central Daylight Saving Time. - /// - internal static string Australian_Central_Daylight_Saving_Time { - get { - return ResourceManager.GetString("Australian Central Daylight Saving Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Central Standard Time. - /// - internal static string Australian_Central_Standard_Time { - get { - return ResourceManager.GetString("Australian Central Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Central Western Standard Time. - /// - internal static string Australian_Central_Western_Standard_Time { - get { - return ResourceManager.GetString("Australian Central Western Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Eastern Daylight Saving Time. - /// - internal static string Australian_Eastern_Daylight_Saving_Time { - get { - return ResourceManager.GetString("Australian Eastern Daylight Saving Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Eastern Standard Time. - /// - internal static string Australian_Eastern_Standard_Time { - get { - return ResourceManager.GetString("Australian Eastern Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Eastern Time. - /// - internal static string Australian_Eastern_Time { - get { - return ResourceManager.GetString("Australian Eastern Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Australian Western Standard Time. - /// - internal static string Australian_Western_Standard_Time { - get { - return ResourceManager.GetString("Australian Western Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Austria. - /// - internal static string Austria { - get { - return ResourceManager.GetString("Austria", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Azerbaijan. - /// - internal static string Azerbaijan { - get { - return ResourceManager.GetString("Azerbaijan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Azerbaijan Time. - /// - internal static string Azerbaijan_Time { - get { - return ResourceManager.GetString("Azerbaijan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Azores (Portugal). - /// - internal static string Azores__Portugal_ { - get { - return ResourceManager.GetString("Azores (Portugal)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Azores Standard Time. - /// - internal static string Azores_Standard_Time { - get { - return ResourceManager.GetString("Azores Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Azores Summer Time. - /// - internal static string Azores_Summer_Time { - get { - return ResourceManager.GetString("Azores Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bahamas. - /// - internal static string Bahamas { - get { - return ResourceManager.GetString("Bahamas", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bahrain. - /// - internal static string Bahrain { - get { - return ResourceManager.GetString("Bahrain", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Baikonur (Kazakhstan). - /// - internal static string Baikonur__Kazakhstan_ { - get { - return ResourceManager.GetString("Baikonur (Kazakhstan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Baja California (Mexico). - /// - internal static string Baja_California__Mexico_ { - get { - return ResourceManager.GetString("Baja California (Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Baja California Sur (Mexico). - /// - internal static string Baja_California_Sur__Mexico_ { - get { - return ResourceManager.GetString("Baja California Sur (Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Baker Island. - /// - internal static string Baker_Island { - get { - return ResourceManager.GetString("Baker Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Baker Island Time. - /// - internal static string Baker_Island_Time { - get { - return ResourceManager.GetString("Baker Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bali (Indonesia). - /// - internal static string Bali__Indonesia_ { - get { - return ResourceManager.GetString("Bali (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bangladesh. - /// - internal static string Bangladesh { - get { - return ResourceManager.GetString("Bangladesh", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bangladesh Standard Time. - /// - internal static string Bangladesh_Standard_Time { - get { - return ResourceManager.GetString("Bangladesh Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Barbados. - /// - internal static string Barbados { - get { - return ResourceManager.GetString("Barbados", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bashkortostan (Russia). - /// - internal static string Bashkortostan__Russia_ { - get { - return ResourceManager.GetString("Bashkortostan (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bayan-Ölgii (Mongolia). - /// - internal static string Bayan_Ölgii__Mongolia_ { - get { - return ResourceManager.GetString("Bayan-Ölgii (Mongolia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Belarus. - /// - internal static string Belarus { - get { - return ResourceManager.GetString("Belarus", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Belgium. - /// - internal static string Belgium { - get { - return ResourceManager.GetString("Belgium", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Belize. - /// - internal static string Belize { - get { - return ResourceManager.GetString("Belize", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Benin. - /// - internal static string Benin { - get { - return ResourceManager.GetString("Benin", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bermuda. - /// - internal static string Bermuda { - get { - return ResourceManager.GetString("Bermuda", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bhutan. - /// - internal static string Bhutan { - get { - return ResourceManager.GetString("Bhutan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bhutan Time. - /// - internal static string Bhutan_Time { - get { - return ResourceManager.GetString("Bhutan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bolivia. - /// - internal static string Bolivia { - get { - return ResourceManager.GetString("Bolivia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bolivia Time. - /// - internal static string Bolivia_Time { - get { - return ResourceManager.GetString("Bolivia Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Border Village (Australia). - /// - internal static string Border_Village__Australia_ { - get { - return ResourceManager.GetString("Border Village (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bosnia and Herzegovina. - /// - internal static string Bosnia_and_Herzegovina { - get { - return ResourceManager.GetString("Bosnia and Herzegovina", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Botswana. - /// - internal static string Botswana { - get { - return ResourceManager.GetString("Botswana", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bougainville (Papua New Guinea). - /// - internal static string Bougainville__Papua_New_Guinea_ { - get { - return ResourceManager.GetString("Bougainville (Papua New Guinea)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bougainville Standard Time. - /// - internal static string Bougainville_Standard_Time { - get { - return ResourceManager.GetString("Bougainville Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Brasília Summer Time. - /// - internal static string Brasília_Summer_Time { - get { - return ResourceManager.GetString("Brasília Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Brasília Time. - /// - internal static string Brasília_Time { - get { - return ResourceManager.GetString("Brasília Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bravo Time Zone. - /// - internal static string Bravo_Time_Zone { - get { - return ResourceManager.GetString("Bravo Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Brazil (most). - /// - internal static string Brazil__most_ { - get { - return ResourceManager.GetString("Brazil (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Columbia (most, Canada). - /// - internal static string British_Columbia__most__Canada_ { - get { - return ResourceManager.GetString("British Columbia (most, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Columbia (northeast, Canada). - /// - internal static string British_Columbia__northeast__Canada_ { - get { - return ResourceManager.GetString("British Columbia (northeast, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Columbia (southeast, Canada). - /// - internal static string British_Columbia__southeast__Canada_ { - get { - return ResourceManager.GetString("British Columbia (southeast, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Indian Ocean Territory. - /// - internal static string British_Indian_Ocean_Territory { - get { - return ResourceManager.GetString("British Indian Ocean Territory", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Indian Ocean Time. - /// - internal static string British_Indian_Ocean_Time { - get { - return ResourceManager.GetString("British Indian Ocean Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Summer Time. - /// - internal static string British_Summer_Time { - get { - return ResourceManager.GetString("British Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to British Virgin Islands. - /// - internal static string British_Virgin_Islands { - get { - return ResourceManager.GetString("British Virgin Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Brunei. - /// - internal static string Brunei { - get { - return ResourceManager.GetString("Brunei", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Brunei Time. - /// - internal static string Brunei_Time { - get { - return ResourceManager.GetString("Brunei Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Bulgaria. - /// - internal static string Bulgaria { - get { - return ResourceManager.GetString("Bulgaria", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Burkina Faso. - /// - internal static string Burkina_Faso { - get { - return ResourceManager.GetString("Burkina Faso", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Burundi. - /// - internal static string Burundi { - get { - return ResourceManager.GetString("Burundi", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Buryatia (Russia). - /// - internal static string Buryatia__Russia_ { - get { - return ResourceManager.GetString("Buryatia (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Caiguna (Australia). - /// - internal static string Caiguna__Australia_ { - get { - return ResourceManager.GetString("Caiguna (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to California (United States). - /// - internal static string California__United_States_ { - get { - return ResourceManager.GetString("California (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cambodia. - /// - internal static string Cambodia { - get { - return ResourceManager.GetString("Cambodia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cameroon. - /// - internal static string Cameroon { - get { - return ResourceManager.GetString("Cameroon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Canary Islands. - /// - internal static string Canary_Islands { - get { - return ResourceManager.GetString("Canary Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cape Verde. - /// - internal static string Cape_Verde { - get { - return ResourceManager.GetString("Cape Verde", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cape Verde Time. - /// - internal static string Cape_Verde_Time { - get { - return ResourceManager.GetString("Cape Verde Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Caribbean Netherlands. - /// - internal static string Caribbean_Netherlands { - get { - return ResourceManager.GetString("Caribbean Netherlands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cayman Islands. - /// - internal static string Cayman_Islands { - get { - return ResourceManager.GetString("Cayman Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Africa Time. - /// - internal static string Central_Africa_Time { - get { - return ResourceManager.GetString("Central Africa Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central African Republic. - /// - internal static string Central_African_Republic { - get { - return ResourceManager.GetString("Central African Republic", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Daylight Time. - /// - internal static string Central_Daylight_Time { - get { - return ResourceManager.GetString("Central Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central European Summer Time. - /// - internal static string Central_European_Summer_Time { - get { - return ResourceManager.GetString("Central European Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central European Time. - /// - internal static string Central_European_Time { - get { - return ResourceManager.GetString("Central European Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Indonesian Time. - /// - internal static string Central_Indonesian_Time { - get { - return ResourceManager.GetString("Central Indonesian Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Kalimantan (Indonesia). - /// - internal static string Central_Kalimantan__Indonesia_ { - get { - return ResourceManager.GetString("Central Kalimantan (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Standard Time. - /// - internal static string Central_Standard_Time { - get { - return ResourceManager.GetString("Central Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Time Zone. - /// - internal static string Central_Time_Zone { - get { - return ResourceManager.GetString("Central Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Central Western Standard Time. - /// - internal static string Central_Western_Standard_Time { - get { - return ResourceManager.GetString("Central Western Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chad. - /// - internal static string Chad { - get { - return ResourceManager.GetString("Chad", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chamorro Standard Time. - /// - internal static string Chamorro_Standard_Time { - get { - return ResourceManager.GetString("Chamorro Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chamorro Time Zone. - /// - internal static string Chamorro_Time_Zone { - get { - return ResourceManager.GetString("Chamorro Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Charlie Time Zone. - /// - internal static string Charlie_Time_Zone { - get { - return ResourceManager.GetString("Charlie Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chatham Daylight Time. - /// - internal static string Chatham_Daylight_Time { - get { - return ResourceManager.GetString("Chatham Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chatham Islands (New Zealand). - /// - internal static string Chatham_Islands__New_Zealand_ { - get { - return ResourceManager.GetString("Chatham Islands (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chatham Standard Time. - /// - internal static string Chatham_Standard_Time { - get { - return ResourceManager.GetString("Chatham Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chelyabinsk (Russia). - /// - internal static string Chelyabinsk__Russia_ { - get { - return ResourceManager.GetString("Chelyabinsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chihuahua (Mexico). - /// - internal static string Chihuahua__Mexico_ { - get { - return ResourceManager.GetString("Chihuahua (Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chile (most). - /// - internal static string Chile__most_ { - get { - return ResourceManager.GetString("Chile (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chile Summer Time. - /// - internal static string Chile_Summer_Time { - get { - return ResourceManager.GetString("Chile Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to China. - /// - internal static string China { - get { - return ResourceManager.GetString("China", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to China Standard Time. - /// - internal static string China_Standard_Time { - get { - return ResourceManager.GetString("China Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Choibalsan Standard Time. - /// - internal static string Choibalsan_Standard_Time { - get { - return ResourceManager.GetString("Choibalsan Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Choibalsan Summer Time. - /// - internal static string Choibalsan_Summer_Time { - get { - return ResourceManager.GetString("Choibalsan Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Christmas Island. - /// - internal static string Christmas_Island { - get { - return ResourceManager.GetString("Christmas Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Christmas Island Time. - /// - internal static string Christmas_Island_Time { - get { - return ResourceManager.GetString("Christmas Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chukotka (Russia). - /// - internal static string Chukotka__Russia_ { - get { - return ResourceManager.GetString("Chukotka (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chuuk (Micronesia). - /// - internal static string Chuuk__Micronesia_ { - get { - return ResourceManager.GetString("Chuuk (Micronesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Chuuk Time. - /// - internal static string Chuuk_Time { - get { - return ResourceManager.GetString("Chuuk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Clipperton Island. - /// - internal static string Clipperton_Island { - get { - return ResourceManager.GetString("Clipperton Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Clipperton Island Standard Time. - /// - internal static string Clipperton_Island_Standard_Time { - get { - return ResourceManager.GetString("Clipperton Island Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cocklebiddy (Australia). - /// - internal static string Cocklebiddy__Australia_ { - get { - return ResourceManager.GetString("Cocklebiddy (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cocos Islands. - /// - internal static string Cocos_Islands { - get { - return ResourceManager.GetString("Cocos Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cocos Islands Time. - /// - internal static string Cocos_Islands_Time { - get { - return ResourceManager.GetString("Cocos Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colombia. - /// - internal static string Colombia { - get { - return ResourceManager.GetString("Colombia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colombia Summer Time. - /// - internal static string Colombia_Summer_Time { - get { - return ResourceManager.GetString("Colombia Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colombia Time. - /// - internal static string Colombia_Time { - get { - return ResourceManager.GetString("Colombia Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Colorado (United States). - /// - internal static string Colorado__United_States_ { - get { - return ResourceManager.GetString("Colorado (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Comoros. - /// - internal static string Comoros { - get { - return ResourceManager.GetString("Comoros", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Congo. - /// - internal static string Congo { - get { - return ResourceManager.GetString("Congo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Connecticut (United States). - /// - internal static string Connecticut__United_States_ { - get { - return ResourceManager.GetString("Connecticut (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cook Islands. - /// - internal static string Cook_Islands { - get { - return ResourceManager.GetString("Cook Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Coordinated Universal Time. - /// - internal static string Coordinated_Universal_Time { - get { - return ResourceManager.GetString("Coordinated Universal Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Copy time. - /// - internal static string CopyTime { - get { - return ResourceManager.GetString("CopyTime", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Costa Rica. - /// - internal static string Costa_Rica { - get { - return ResourceManager.GetString("Costa Rica", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Countries. - /// - internal static string Countries { - get { - return ResourceManager.GetString("Countries", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Countries with daylight saving time. - /// - internal static string CountriesWithDst { - get { - return ResourceManager.GetString("CountriesWithDst", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Crimea. - /// - internal static string Crimea { - get { - return ResourceManager.GetString("Crimea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Croatia. - /// - internal static string Croatia { - get { - return ResourceManager.GetString("Croatia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Crozet Islands (French Southern and Antarctic Lands). - /// - internal static string Crozet_Islands__French_Southern_and_Antarctic_Lands_ { - get { - return ResourceManager.GetString("Crozet Islands (French Southern and Antarctic Lands)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cuba. - /// - internal static string Cuba { - get { - return ResourceManager.GetString("Cuba", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cuba Daylight Time. - /// - internal static string Cuba_Daylight_Time { - get { - return ResourceManager.GetString("Cuba Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cuba Standard Time. - /// - internal static string Cuba_Standard_Time { - get { - return ResourceManager.GetString("Cuba Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Curaçao. - /// - internal static string Curaçao { - get { - return ResourceManager.GetString("Curaçao", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cyprus. - /// - internal static string Cyprus { - get { - return ResourceManager.GetString("Cyprus", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Czech Republic. - /// - internal static string Czech_Republic { - get { - return ResourceManager.GetString("Czech Republic", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Danmarkshavn. - /// - internal static string Danmarkshavn { - get { - return ResourceManager.GetString("Danmarkshavn", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Davis Time. - /// - internal static string Davis_Time { - get { - return ResourceManager.GetString("Davis Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Daylight Time. - /// - internal static string DaylightTime { - get { - return ResourceManager.GetString("DaylightTime", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to DT. - /// - internal static string DaylightTimeShortcut { - get { - return ResourceManager.GetString("DaylightTimeShortcut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delaware (United States). - /// - internal static string Delaware__United_States_ { - get { - return ResourceManager.GetString("Delaware (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Delta Time Zone. - /// - internal static string Delta_Time_Zone { - get { - return ResourceManager.GetString("Delta Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Democratic Republic of the Congo (most). - /// - internal static string Democratic_Republic_of_the_Congo__most_ { - get { - return ResourceManager.GetString("Democratic Republic of the Congo (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Denmark. - /// - internal static string Denmark { - get { - return ResourceManager.GetString("Denmark", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to District of Columbia (United States). - /// - internal static string District_of_Columbia__United_States_ { - get { - return ResourceManager.GetString("District of Columbia (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Djibouti. - /// - internal static string Djibouti { - get { - return ResourceManager.GetString("Djibouti", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dominica. - /// - internal static string Dominica { - get { - return ResourceManager.GetString("Dominica", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dominican Republic. - /// - internal static string Dominican_Republic { - get { - return ResourceManager.GetString("Dominican Republic", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Donetsk PR. - /// - internal static string Donetsk_PR { - get { - return ResourceManager.GetString("Donetsk PR", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Dumont d'Urville Time. - /// - internal static string Dumont_d_Urville_Time { - get { - return ResourceManager.GetString("Dumont d\'Urville Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to East Africa Time. - /// - internal static string East_Africa_Time { - get { - return ResourceManager.GetString("East Africa Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to East Kalimantan (Indonesia). - /// - internal static string East_Kalimantan__Indonesia_ { - get { - return ResourceManager.GetString("East Kalimantan (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to East Nusa Tenggara (Indonesia). - /// - internal static string East_Nusa_Tenggara__Indonesia_ { - get { - return ResourceManager.GetString("East Nusa Tenggara (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to East Timor. - /// - internal static string East_Timor { - get { - return ResourceManager.GetString("East Timor", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Easter Island (Chile). - /// - internal static string Easter_Island__Chile_ { - get { - return ResourceManager.GetString("Easter Island (Chile)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Easter Island Standard Time. - /// - internal static string Easter_Island_Standard_Time { - get { - return ResourceManager.GetString("Easter Island Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Easter Island Summer Time. - /// - internal static string Easter_Island_Summer_Time { - get { - return ResourceManager.GetString("Easter Island Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Caribbean Time. - /// - internal static string Eastern_Caribbean_Time { - get { - return ResourceManager.GetString("Eastern Caribbean Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Daylight Time. - /// - internal static string Eastern_Daylight_Time { - get { - return ResourceManager.GetString("Eastern Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern European Summer Time. - /// - internal static string Eastern_European_Summer_Time { - get { - return ResourceManager.GetString("Eastern European Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern European Time. - /// - internal static string Eastern_European_Time { - get { - return ResourceManager.GetString("Eastern European Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Greenland Summer Time. - /// - internal static string Eastern_Greenland_Summer_Time { - get { - return ResourceManager.GetString("Eastern Greenland Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Greenland Time. - /// - internal static string Eastern_Greenland_Time { - get { - return ResourceManager.GetString("Eastern Greenland Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Indonesian Time. - /// - internal static string Eastern_Indonesian_Time { - get { - return ResourceManager.GetString("Eastern Indonesian Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Standard Time. - /// - internal static string Eastern_Standard_Time { - get { - return ResourceManager.GetString("Eastern Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eastern Time Zone. - /// - internal static string Eastern_Time_Zone { - get { - return ResourceManager.GetString("Eastern Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Echo Time Zone. - /// - internal static string Echo_Time_Zone { - get { - return ResourceManager.GetString("Echo Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ecuador: Galápagos. - /// - internal static string Ecuador__Galápagos { - get { - return ResourceManager.GetString("Ecuador: Galápagos", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ecuador (most). - /// - internal static string Ecuador__most_ { - get { - return ResourceManager.GetString("Ecuador (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ecuador Time. - /// - internal static string Ecuador_Time { - get { - return ResourceManager.GetString("Ecuador Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Egypt. - /// - internal static string Egypt { - get { - return ResourceManager.GetString("Egypt", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to El Salvador. - /// - internal static string El_Salvador { - get { - return ResourceManager.GetString("El Salvador", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Équateur (Democratic Republic of the Congo). - /// - internal static string Équateur__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Équateur (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Equatorial Guinea. - /// - internal static string Equatorial_Guinea { - get { - return ResourceManager.GetString("Equatorial Guinea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eritrea. - /// - internal static string Eritrea { - get { - return ResourceManager.GetString("Eritrea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Estonia. - /// - internal static string Estonia { - get { - return ResourceManager.GetString("Estonia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eswatini. - /// - internal static string Eswatini { - get { - return ResourceManager.GetString("Eswatini", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ethiopia. - /// - internal static string Ethiopia { - get { - return ResourceManager.GetString("Ethiopia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Eucla (Australia). - /// - internal static string Eucla__Australia_ { - get { - return ResourceManager.GetString("Eucla (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Falkland Islands. - /// - internal static string Falkland_Islands { - get { - return ResourceManager.GetString("Falkland Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Falkland Islands Summer Time. - /// - internal static string Falkland_Islands_Summer_Time { - get { - return ResourceManager.GetString("Falkland Islands Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Falkland Islands Time. - /// - internal static string Falkland_Islands_Time { - get { - return ResourceManager.GetString("Falkland Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Faroe Islands. - /// - internal static string Faroe_Islands { - get { - return ResourceManager.GetString("Faroe Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fernando de Noronha (Brazil). - /// - internal static string Fernando_de_Noronha__Brazil_ { - get { - return ResourceManager.GetString("Fernando de Noronha (Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fernando de Noronha Time. - /// - internal static string Fernando_de_Noronha_Time { - get { - return ResourceManager.GetString("Fernando de Noronha Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fiji. - /// - internal static string Fiji { - get { - return ResourceManager.GetString("Fiji", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Fiji Time. - /// - internal static string Fiji_Time { - get { - return ResourceManager.GetString("Fiji Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Finland. - /// - internal static string Finland { - get { - return ResourceManager.GetString("Finland", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Florida (most, United States). - /// - internal static string Florida__most__United_States_ { - get { - return ResourceManager.GetString("Florida (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Forty-Fours (New Zealand). - /// - internal static string Forty_Fours__New_Zealand_ { - get { - return ResourceManager.GetString("Forty-Fours (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Foxtrot Time Zone. - /// - internal static string Foxtrot_Time_Zone { - get { - return ResourceManager.GetString("Foxtrot Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to France (metropolitan). - /// - internal static string France__metropolitan_ { - get { - return ResourceManager.GetString("France (metropolitan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to French Guiana. - /// - internal static string French_Guiana { - get { - return ResourceManager.GetString("French Guiana", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to French Guiana Time. - /// - internal static string French_Guiana_Time { - get { - return ResourceManager.GetString("French Guiana Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to French Polynesia (most). - /// - internal static string French_Polynesia__most_ { - get { - return ResourceManager.GetString("French Polynesia (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to French Southern and Antarctic Time. - /// - internal static string French_Southern_and_Antarctic_Time { - get { - return ResourceManager.GetString("French Southern and Antarctic Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Further-eastern European Time. - /// - internal static string Further_eastern_European_Time { - get { - return ResourceManager.GetString("Further-eastern European Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gabon. - /// - internal static string Gabon { - get { - return ResourceManager.GetString("Gabon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Galápagos Time. - /// - internal static string Galápagos_Time { - get { - return ResourceManager.GetString("Galápagos Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gambia. - /// - internal static string Gambia { - get { - return ResourceManager.GetString("Gambia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gambier Islands (French Polynesia). - /// - internal static string Gambier_Islands__French_Polynesia_ { - get { - return ResourceManager.GetString("Gambier Islands (French Polynesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gambier Islands Time. - /// - internal static string Gambier_Islands_Time { - get { - return ResourceManager.GetString("Gambier Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Georgia. - /// - internal static string Georgia { - get { - return ResourceManager.GetString("Georgia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Georgia (United States). - /// - internal static string Georgia__United_States_ { - get { - return ResourceManager.GetString("Georgia (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Georgia Standard Time. - /// - internal static string Georgia_Standard_Time { - get { - return ResourceManager.GetString("Georgia Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Germany. - /// - internal static string Germany { - get { - return ResourceManager.GetString("Germany", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ghana. - /// - internal static string Ghana { - get { - return ResourceManager.GetString("Ghana", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gibraltar. - /// - internal static string Gibraltar { - get { - return ResourceManager.GetString("Gibraltar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gilbert Island Time. - /// - internal static string Gilbert_Island_Time { - get { - return ResourceManager.GetString("Gilbert Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gilbert Islands (Kiribati). - /// - internal static string Gilbert_Islands__Kiribati_ { - get { - return ResourceManager.GetString("Gilbert Islands (Kiribati)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Golf Time Zone. - /// - internal static string Golf_Time_Zone { - get { - return ResourceManager.GetString("Golf Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Greece. - /// - internal static string Greece { - get { - return ResourceManager.GetString("Greece", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Greenland (most). - /// - internal static string Greenland__most_ { - get { - return ResourceManager.GetString("Greenland (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Greenwich Mean Time. - /// - internal static string Greenwich_Mean_Time { - get { - return ResourceManager.GetString("Greenwich Mean Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Grenada. - /// - internal static string Grenada { - get { - return ResourceManager.GetString("Grenada", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guadeloupe. - /// - internal static string Guadeloupe { - get { - return ResourceManager.GetString("Guadeloupe", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guam. - /// - internal static string Guam { - get { - return ResourceManager.GetString("Guam", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guatemala. - /// - internal static string Guatemala { - get { - return ResourceManager.GetString("Guatemala", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guernsey. - /// - internal static string Guernsey { - get { - return ResourceManager.GetString("Guernsey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guinea. - /// - internal static string Guinea { - get { - return ResourceManager.GetString("Guinea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guinea-Bissau. - /// - internal static string Guinea_Bissau { - get { - return ResourceManager.GetString("Guinea-Bissau", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gulf Standard Time. - /// - internal static string Gulf_Standard_Time { - get { - return ResourceManager.GetString("Gulf Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guyana. - /// - internal static string Guyana { - get { - return ResourceManager.GetString("Guyana", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Guyana Time. - /// - internal static string Guyana_Time { - get { - return ResourceManager.GetString("Guyana Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Haiti. - /// - internal static string Haiti { - get { - return ResourceManager.GetString("Haiti", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hawaii (United States). - /// - internal static string Hawaii__United_States_ { - get { - return ResourceManager.GetString("Hawaii (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hawaii-Aleutian Daylight Time. - /// - internal static string Hawaii_Aleutian_Daylight_Time { - get { - return ResourceManager.GetString("Hawaii-Aleutian Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hawaii-Aleutian Standard Time. - /// - internal static string Hawaii_Aleutian_Standard_Time { - get { - return ResourceManager.GetString("Hawaii-Aleutian Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hawaii-Aleutian Time Zone. - /// - internal static string Hawaii_Aleutian_Time_Zone { - get { - return ResourceManager.GetString("Hawaii-Aleutian Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Heard and McDonald Islands Time. - /// - internal static string Heard_and_McDonald_Islands_Time { - get { - return ResourceManager.GetString("Heard and McDonald Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Heard Island and McDonald Islands. - /// - internal static string Heard_Island_and_McDonald_Islands { - get { - return ResourceManager.GetString("Heard Island and McDonald Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Heure Avancée d'Europe Centrale. - /// - internal static string Heure_Avancée_d_Europe_Centrale { - get { - return ResourceManager.GetString("Heure Avancée d\'Europe Centrale", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Honduras. - /// - internal static string Honduras { - get { - return ResourceManager.GetString("Honduras", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hong Kong. - /// - internal static string Hong_Kong { - get { - return ResourceManager.GetString("Hong Kong", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hong Kong Time. - /// - internal static string Hong_Kong_Time { - get { - return ResourceManager.GetString("Hong Kong Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hotel Time Zone. - /// - internal static string Hotel_Time_Zone { - get { - return ResourceManager.GetString("Hotel Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hovd Time. - /// - internal static string Hovd_Time { - get { - return ResourceManager.GetString("Hovd Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Howland Island. - /// - internal static string Howland_Island { - get { - return ResourceManager.GetString("Howland Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hungary. - /// - internal static string Hungary { - get { - return ResourceManager.GetString("Hungary", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Iceland. - /// - internal static string Iceland { - get { - return ResourceManager.GetString("Iceland", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Idaho (most, United States). - /// - internal static string Idaho__most__United_States_ { - get { - return ResourceManager.GetString("Idaho (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Idaho (north, United States). - /// - internal static string Idaho__north__United_States_ { - get { - return ResourceManager.GetString("Idaho (north, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Illinois (United States). - /// - internal static string Illinois__United_States_ { - get { - return ResourceManager.GetString("Illinois (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to India. - /// - internal static string India { - get { - return ResourceManager.GetString("India", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to India Time Zone. - /// - internal static string India_Time_Zone { - get { - return ResourceManager.GetString("India Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Indian Ocean Time. - /// - internal static string Indian_Ocean_Time { - get { - return ResourceManager.GetString("Indian Ocean Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Indian Standard Time. - /// - internal static string Indian_Standard_Time { - get { - return ResourceManager.GetString("Indian Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Indiana (most, United States). - /// - internal static string Indiana__most__United_States_ { - get { - return ResourceManager.GetString("Indiana (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Indochina Time. - /// - internal static string Indochina_Time { - get { - return ResourceManager.GetString("Indochina Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to International Date Line West time zone. - /// - internal static string International_Date_Line_West_time_zone { - get { - return ResourceManager.GetString("International Date Line West time zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Iowa (United States). - /// - internal static string Iowa__United_States_ { - get { - return ResourceManager.GetString("Iowa (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Iran. - /// - internal static string Iran { - get { - return ResourceManager.GetString("Iran", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Iran Daylight Time. - /// - internal static string Iran_Daylight_Time { - get { - return ResourceManager.GetString("Iran Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Iran Standard Time. - /// - internal static string Iran_Standard_Time { - get { - return ResourceManager.GetString("Iran Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Iraq. - /// - internal static string Iraq { - get { - return ResourceManager.GetString("Iraq", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ireland. - /// - internal static string Ireland { - get { - return ResourceManager.GetString("Ireland", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Irish Standard Time. - /// - internal static string Irish_Standard_Time { - get { - return ResourceManager.GetString("Irish Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Irkutsk (Russia). - /// - internal static string Irkutsk__Russia_ { - get { - return ResourceManager.GetString("Irkutsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Irkutsk Time. - /// - internal static string Irkutsk_Time { - get { - return ResourceManager.GetString("Irkutsk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Islands of Four Mountains (United States). - /// - internal static string Islands_of_Four_Mountains__United_States_ { - get { - return ResourceManager.GetString("Islands of Four Mountains (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Isle of Man. - /// - internal static string Isle_of_Man { - get { - return ResourceManager.GetString("Isle of Man", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Israel. - /// - internal static string Israel { - get { - return ResourceManager.GetString("Israel", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Israel Daylight Time. - /// - internal static string Israel_Daylight_Time { - get { - return ResourceManager.GetString("Israel Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Israel Standard Time. - /// - internal static string Israel_Standard_Time { - get { - return ResourceManager.GetString("Israel Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Italy. - /// - internal static string Italy { - get { - return ResourceManager.GetString("Italy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ittoqqortoormiit (Greenland). - /// - internal static string Ittoqqortoormiit__Greenland_ { - get { - return ResourceManager.GetString("Ittoqqortoormiit (Greenland)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ivory Coast. - /// - internal static string Ivory_Coast { - get { - return ResourceManager.GetString("Ivory Coast", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Jamaica. - /// - internal static string Jamaica { - get { - return ResourceManager.GetString("Jamaica", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Japan. - /// - internal static string Japan { - get { - return ResourceManager.GetString("Japan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Japan Standard Time. - /// - internal static string Japan_Standard_Time { - get { - return ResourceManager.GetString("Japan Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Jarvis Island. - /// - internal static string Jarvis_Island { - get { - return ResourceManager.GetString("Jarvis Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Java (Indonesia). - /// - internal static string Java__Indonesia_ { - get { - return ResourceManager.GetString("Java (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Jersey. - /// - internal static string Jersey { - get { - return ResourceManager.GetString("Jersey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Jervis Bay Territory (Australia). - /// - internal static string Jervis_Bay_Territory__Australia_ { - get { - return ResourceManager.GetString("Jervis Bay Territory (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Jewish (Russia). - /// - internal static string Jewish__Russia_ { - get { - return ResourceManager.GetString("Jewish (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Johnston Atoll. - /// - internal static string Johnston_Atoll { - get { - return ResourceManager.GetString("Johnston Atoll", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Jordan. - /// - internal static string Jordan { - get { - return ResourceManager.GetString("Jordan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kaliningrad (Russia). - /// - internal static string Kaliningrad__Russia_ { - get { - return ResourceManager.GetString("Kaliningrad (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kaliningrad Time. - /// - internal static string Kaliningrad_Time { - get { - return ResourceManager.GetString("Kaliningrad Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kamchatka (Russia). - /// - internal static string Kamchatka__Russia_ { - get { - return ResourceManager.GetString("Kamchatka (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kamchatka Time. - /// - internal static string Kamchatka_Time { - get { - return ResourceManager.GetString("Kamchatka Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kansas (most, United States). - /// - internal static string Kansas__most__United_States_ { - get { - return ResourceManager.GetString("Kansas (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kazakhstan (most). - /// - internal static string Kazakhstan__most_ { - get { - return ResourceManager.GetString("Kazakhstan (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kemerovo (Russia). - /// - internal static string Kemerovo__Russia_ { - get { - return ResourceManager.GetString("Kemerovo (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kentucky (most, United States). - /// - internal static string Kentucky__most__United_States_ { - get { - return ResourceManager.GetString("Kentucky (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kenya. - /// - internal static string Kenya { - get { - return ResourceManager.GetString("Kenya", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kerguelen Islands (French Southern and Antarctic Lands). - /// - internal static string Kerguelen_Islands__French_Southern_and_Antarctic_Lands_ { - get { - return ResourceManager.GetString("Kerguelen Islands (French Southern and Antarctic Lands)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Khabarovsk (Russia). - /// - internal static string Khabarovsk__Russia_ { - get { - return ResourceManager.GetString("Khabarovsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Khakassia (Russia). - /// - internal static string Khakassia__Russia_ { - get { - return ResourceManager.GetString("Khakassia (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Khanty-Mansi (Russia). - /// - internal static string Khanty_Mansi__Russia_ { - get { - return ResourceManager.GetString("Khanty-Mansi (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Khovd (Mongolia). - /// - internal static string Khovd__Mongolia_ { - get { - return ResourceManager.GetString("Khovd (Mongolia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kilo Time Zone. - /// - internal static string Kilo_Time_Zone { - get { - return ResourceManager.GetString("Kilo Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kingman Reef. - /// - internal static string Kingman_Reef { - get { - return ResourceManager.GetString("Kingman Reef", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kinshasa (Democratic Republic of the Congo). - /// - internal static string Kinshasa__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Kinshasa (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kongo Central (Democratic Republic of the Congo). - /// - internal static string Kongo_Central__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Kongo Central (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Korea Standard Time. - /// - internal static string Korea_Standard_Time { - get { - return ResourceManager.GetString("Korea Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kosovo. - /// - internal static string Kosovo { - get { - return ResourceManager.GetString("Kosovo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kosrae (Micronesia). - /// - internal static string Kosrae__Micronesia_ { - get { - return ResourceManager.GetString("Kosrae (Micronesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kosrae Time. - /// - internal static string Kosrae_Time { - get { - return ResourceManager.GetString("Kosrae Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Krasnoyarsk (Russia). - /// - internal static string Krasnoyarsk__Russia_ { - get { - return ResourceManager.GetString("Krasnoyarsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Krasnoyarsk Time. - /// - internal static string Krasnoyarsk_Time { - get { - return ResourceManager.GetString("Krasnoyarsk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kurgan (Russia). - /// - internal static string Kurgan__Russia_ { - get { - return ResourceManager.GetString("Kurgan (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kuwait. - /// - internal static string Kuwait { - get { - return ResourceManager.GetString("Kuwait", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kwango (Democratic Republic of the Congo). - /// - internal static string Kwango__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Kwango (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kwilu (Democratic Republic of the Congo). - /// - internal static string Kwilu__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Kwilu (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kyrgyzstan. - /// - internal static string Kyrgyzstan { - get { - return ResourceManager.GetString("Kyrgyzstan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kyrgyzstan Time. - /// - internal static string Kyrgyzstan_Time { - get { - return ResourceManager.GetString("Kyrgyzstan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kyzylorda (Kazakhstan). - /// - internal static string Kyzylorda__Kazakhstan_ { - get { - return ResourceManager.GetString("Kyzylorda (Kazakhstan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Labrador (most, Canada). - /// - internal static string Labrador__most__Canada_ { - get { - return ResourceManager.GetString("Labrador (most, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Labrador (southeast, Canada). - /// - internal static string Labrador__southeast__Canada_ { - get { - return ResourceManager.GetString("Labrador (southeast, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Laos. - /// - internal static string Laos { - get { - return ResourceManager.GetString("Laos", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Latvia. - /// - internal static string Latvia { - get { - return ResourceManager.GetString("Latvia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lebanon. - /// - internal static string Lebanon { - get { - return ResourceManager.GetString("Lebanon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lesotho. - /// - internal static string Lesotho { - get { - return ResourceManager.GetString("Lesotho", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Liberia. - /// - internal static string Liberia { - get { - return ResourceManager.GetString("Liberia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Libya. - /// - internal static string Libya { - get { - return ResourceManager.GetString("Libya", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Liechtenstein. - /// - internal static string Liechtenstein { - get { - return ResourceManager.GetString("Liechtenstein", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lima Time Zone. - /// - internal static string Lima_Time_Zone { - get { - return ResourceManager.GetString("Lima Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Line Islands (Kiribati). - /// - internal static string Line_Islands__Kiribati_ { - get { - return ResourceManager.GetString("Line Islands (Kiribati)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Line Islands Time. - /// - internal static string Line_Islands_Time { - get { - return ResourceManager.GetString("Line Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lithuania. - /// - internal static string Lithuania { - get { - return ResourceManager.GetString("Lithuania", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Little Mangere Island (New Zealand). - /// - internal static string Little_Mangere_Island__New_Zealand_ { - get { - return ResourceManager.GetString("Little Mangere Island (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lord Howe Island (Australia). - /// - internal static string Lord_Howe_Island__Australia_ { - get { - return ResourceManager.GetString("Lord Howe Island (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lord Howe Standard Time. - /// - internal static string Lord_Howe_Standard_Time { - get { - return ResourceManager.GetString("Lord Howe Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lord Howe Summer Time. - /// - internal static string Lord_Howe_Summer_Time { - get { - return ResourceManager.GetString("Lord Howe Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Louisiana (United States). - /// - internal static string Louisiana__United_States_ { - get { - return ResourceManager.GetString("Louisiana (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Luhansk PR. - /// - internal static string Luhansk_PR { - get { - return ResourceManager.GetString("Luhansk PR", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Luxembourg. - /// - internal static string Luxembourg { - get { - return ResourceManager.GetString("Luxembourg", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Macau. - /// - internal static string Macau { - get { - return ResourceManager.GetString("Macau", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Macquarie Island Station Time. - /// - internal static string Macquarie_Island_Station_Time { - get { - return ResourceManager.GetString("Macquarie Island Station Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Madagascar. - /// - internal static string Madagascar { - get { - return ResourceManager.GetString("Madagascar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Madura (Australia). - /// - internal static string Madura__Australia_ { - get { - return ResourceManager.GetString("Madura (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Magadan (Russia). - /// - internal static string Magadan__Russia_ { - get { - return ResourceManager.GetString("Magadan (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Magadan Time. - /// - internal static string Magadan_Time { - get { - return ResourceManager.GetString("Magadan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Magallanes (Chile). - /// - internal static string Magallanes__Chile_ { - get { - return ResourceManager.GetString("Magallanes (Chile)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mai-Ndombe (Democratic Republic of the Congo). - /// - internal static string Mai_Ndombe__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Mai-Ndombe (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maine (United States). - /// - internal static string Maine__United_States_ { - get { - return ResourceManager.GetString("Maine (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Malawi. - /// - internal static string Malawi { - get { - return ResourceManager.GetString("Malawi", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Malaysia. - /// - internal static string Malaysia { - get { - return ResourceManager.GetString("Malaysia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Malaysia Standard Time. - /// - internal static string Malaysia_Standard_Time { - get { - return ResourceManager.GetString("Malaysia Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Malaysia Time. - /// - internal static string Malaysia_Time { - get { - return ResourceManager.GetString("Malaysia Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maldives. - /// - internal static string Maldives { - get { - return ResourceManager.GetString("Maldives", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maldives Time. - /// - internal static string Maldives_Time { - get { - return ResourceManager.GetString("Maldives Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mali. - /// - internal static string Mali { - get { - return ResourceManager.GetString("Mali", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Malta. - /// - internal static string Malta { - get { - return ResourceManager.GetString("Malta", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maluku (Indonesia). - /// - internal static string Maluku__Indonesia_ { - get { - return ResourceManager.GetString("Maluku (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mangystau (Kazakhstan). - /// - internal static string Mangystau__Kazakhstan_ { - get { - return ResourceManager.GetString("Mangystau (Kazakhstan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Manitoba (Canada). - /// - internal static string Manitoba__Canada_ { - get { - return ResourceManager.GetString("Manitoba (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Marquesas Islands (French Polynesia). - /// - internal static string Marquesas_Islands__French_Polynesia_ { - get { - return ResourceManager.GetString("Marquesas Islands (French Polynesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Marquesas Islands Time. - /// - internal static string Marquesas_Islands_Time { - get { - return ResourceManager.GetString("Marquesas Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Marshall Islands. - /// - internal static string Marshall_Islands { - get { - return ResourceManager.GetString("Marshall Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Marshall Islands Time. - /// - internal static string Marshall_Islands_Time { - get { - return ResourceManager.GetString("Marshall Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Martinique. - /// - internal static string Martinique { - get { - return ResourceManager.GetString("Martinique", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Maryland (United States). - /// - internal static string Maryland__United_States_ { - get { - return ResourceManager.GetString("Maryland (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Massachusetts. - /// - internal static string Massachusetts { - get { - return ResourceManager.GetString("Massachusetts", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mato Grosso (Brazil). - /// - internal static string Mato_Grosso__Brazil_ { - get { - return ResourceManager.GetString("Mato Grosso (Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mato Grosso do Sul (Brazil). - /// - internal static string Mato_Grosso_do_Sul__Brazil_ { - get { - return ResourceManager.GetString("Mato Grosso do Sul (Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mauritania. - /// - internal static string Mauritania { - get { - return ResourceManager.GetString("Mauritania", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mauritius. - /// - internal static string Mauritius { - get { - return ResourceManager.GetString("Mauritius", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mauritius Time. - /// - internal static string Mauritius_Time { - get { - return ResourceManager.GetString("Mauritius Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mawson Station Time. - /// - internal static string Mawson_Station_Time { - get { - return ResourceManager.GetString("Mawson Station Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mayotte. - /// - internal static string Mayotte { - get { - return ResourceManager.GetString("Mayotte", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mexico (most). - /// - internal static string Mexico__most_ { - get { - return ResourceManager.GetString("Mexico (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Michigan (most, United States). - /// - internal static string Michigan__most__United_States_ { - get { - return ResourceManager.GetString("Michigan (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Middle European Summer Time. - /// - internal static string Middle_European_Summer_Time { - get { - return ResourceManager.GetString("Middle European Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Middle European Time. - /// - internal static string Middle_European_Time { - get { - return ResourceManager.GetString("Middle European Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Midway Atoll. - /// - internal static string Midway_Atoll { - get { - return ResourceManager.GetString("Midway Atoll", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mike Time Zone. - /// - internal static string Mike_Time_Zone { - get { - return ResourceManager.GetString("Mike Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Minnesota (United States). - /// - internal static string Minnesota__United_States_ { - get { - return ResourceManager.GetString("Minnesota (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mishkeegogamang (Canada). - /// - internal static string Mishkeegogamang__Canada_ { - get { - return ResourceManager.GetString("Mishkeegogamang (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mississippi (United States). - /// - internal static string Mississippi__United_States_ { - get { - return ResourceManager.GetString("Mississippi (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Missouri (United States). - /// - internal static string Missouri__United_States_ { - get { - return ResourceManager.GetString("Missouri (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Moldova. - /// - internal static string Moldova { - get { - return ResourceManager.GetString("Moldova", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Monaco. - /// - internal static string Monaco { - get { - return ResourceManager.GetString("Monaco", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mongala (Democratic Republic of the Congo). - /// - internal static string Mongala__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Mongala (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mongolia (most). - /// - internal static string Mongolia__most_ { - get { - return ResourceManager.GetString("Mongolia (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Montana (United States). - /// - internal static string Montana__United_States_ { - get { - return ResourceManager.GetString("Montana (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Montenegro. - /// - internal static string Montenegro { - get { - return ResourceManager.GetString("Montenegro", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Montserrat. - /// - internal static string Montserrat { - get { - return ResourceManager.GetString("Montserrat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Morocco. - /// - internal static string Morocco { - get { - return ResourceManager.GetString("Morocco", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Moscow Time. - /// - internal static string Moscow_Time { - get { - return ResourceManager.GetString("Moscow Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mountain Daylight Time. - /// - internal static string Mountain_Daylight_Time { - get { - return ResourceManager.GetString("Mountain Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mountain Standard Time. - /// - internal static string Mountain_Standard_Time { - get { - return ResourceManager.GetString("Mountain Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mountain Time Zone. - /// - internal static string Mountain_Time_Zone { - get { - return ResourceManager.GetString("Mountain Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mozambique. - /// - internal static string Mozambique { - get { - return ResourceManager.GetString("Mozambique", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mundrabilla (Australia). - /// - internal static string Mundrabilla__Australia_ { - get { - return ResourceManager.GetString("Mundrabilla (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Myanmar. - /// - internal static string Myanmar { - get { - return ResourceManager.GetString("Myanmar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Myanmar Standard Time. - /// - internal static string Myanmar_Standard_Time { - get { - return ResourceManager.GetString("Myanmar Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Names. - /// - internal static string Names { - get { - return ResourceManager.GetString("Names", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Namibia. - /// - internal static string Namibia { - get { - return ResourceManager.GetString("Namibia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nauru. - /// - internal static string Nauru { - get { - return ResourceManager.GetString("Nauru", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Navassa Island. - /// - internal static string Navassa_Island { - get { - return ResourceManager.GetString("Navassa Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nayarit (most, Mexico). - /// - internal static string Nayarit__most__Mexico_ { - get { - return ResourceManager.GetString("Nayarit (most, Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Near Islands (United States). - /// - internal static string Near_Islands__United_States_ { - get { - return ResourceManager.GetString("Near Islands (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nebraska (most, United States). - /// - internal static string Nebraska__most__United_States_ { - get { - return ResourceManager.GetString("Nebraska (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nepal. - /// - internal static string Nepal { - get { - return ResourceManager.GetString("Nepal", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nepal Standard Time. - /// - internal static string Nepal_Standard_Time { - get { - return ResourceManager.GetString("Nepal Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Netherlands (European). - /// - internal static string Netherlands__European_ { - get { - return ResourceManager.GetString("Netherlands (European)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nevada (United States). - /// - internal static string Nevada__United_States_ { - get { - return ResourceManager.GetString("Nevada (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Brunswick (Canada). - /// - internal static string New_Brunswick__Canada_ { - get { - return ResourceManager.GetString("New Brunswick (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Caledonia. - /// - internal static string New_Caledonia { - get { - return ResourceManager.GetString("New Caledonia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Caledonia Time. - /// - internal static string New_Caledonia_Time { - get { - return ResourceManager.GetString("New Caledonia Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Hampshire (United States). - /// - internal static string New_Hampshire__United_States_ { - get { - return ResourceManager.GetString("New Hampshire (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Jersey (United States). - /// - internal static string New_Jersey__United_States_ { - get { - return ResourceManager.GetString("New Jersey (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Mexico (United States). - /// - internal static string New_Mexico__United_States_ { - get { - return ResourceManager.GetString("New Mexico (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New South Wales (most, Australia). - /// - internal static string New_South_Wales__most__Australia_ { - get { - return ResourceManager.GetString("New South Wales (most, Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New York (United States). - /// - internal static string New_York__United_States_ { - get { - return ResourceManager.GetString("New York (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Zealand. - /// - internal static string New_Zealand { - get { - return ResourceManager.GetString("New Zealand", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Zealand Daylight Time. - /// - internal static string New_Zealand_Daylight_Time { - get { - return ResourceManager.GetString("New Zealand Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to New Zealand Standard Time. - /// - internal static string New_Zealand_Standard_Time { - get { - return ResourceManager.GetString("New Zealand Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Newfoundland (Canada). - /// - internal static string Newfoundland__Canada_ { - get { - return ResourceManager.GetString("Newfoundland (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Newfoundland Daylight Time. - /// - internal static string Newfoundland_Daylight_Time { - get { - return ResourceManager.GetString("Newfoundland Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Newfoundland Standard Time. - /// - internal static string Newfoundland_Standard_Time { - get { - return ResourceManager.GetString("Newfoundland Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Newfoundland Time Zone. - /// - internal static string Newfoundland_Time_Zone { - get { - return ResourceManager.GetString("Newfoundland Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nicaragua. - /// - internal static string Nicaragua { - get { - return ResourceManager.GetString("Nicaragua", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Niger. - /// - internal static string Niger { - get { - return ResourceManager.GetString("Niger", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nigeria. - /// - internal static string Nigeria { - get { - return ResourceManager.GetString("Nigeria", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Niue. - /// - internal static string Niue { - get { - return ResourceManager.GetString("Niue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Niue Time. - /// - internal static string Niue_Time { - get { - return ResourceManager.GetString("Niue Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No. - /// - internal static string No { - get { - return ResourceManager.GetString("No", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nord-Ubangi (Democratic Republic of the Congo). - /// - internal static string Nord_Ubangi__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Nord-Ubangi (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Norfolk Island. - /// - internal static string Norfolk_Island { - get { - return ResourceManager.GetString("Norfolk Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Norfolk Island Time. - /// - internal static string Norfolk_Island_Time { - get { - return ResourceManager.GetString("Norfolk Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to North Carolina (United States). - /// - internal static string North_Carolina__United_States_ { - get { - return ResourceManager.GetString("North Carolina (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to North Dakota (most, United States). - /// - internal static string North_Dakota__most__United_States_ { - get { - return ResourceManager.GetString("North Dakota (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to North Kalimantan (Indonesia). - /// - internal static string North_Kalimantan__Indonesia_ { - get { - return ResourceManager.GetString("North Kalimantan (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to North Korea. - /// - internal static string North_Korea { - get { - return ResourceManager.GetString("North Korea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to North Macedonia. - /// - internal static string North_Macedonia { - get { - return ResourceManager.GetString("North Macedonia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to North Maluku (Indonesia). - /// - internal static string North_Maluku__Indonesia_ { - get { - return ResourceManager.GetString("North Maluku (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Northern Cyprus. - /// - internal static string Northern_Cyprus { - get { - return ResourceManager.GetString("Northern Cyprus", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Northern Mariana Islands. - /// - internal static string Northern_Mariana_Islands { - get { - return ResourceManager.GetString("Northern Mariana Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Northern Territory. - /// - internal static string Northern_Territory { - get { - return ResourceManager.GetString("Northern Territory", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Northwest Territories (Canada). - /// - internal static string Northwest_Territories__Canada_ { - get { - return ResourceManager.GetString("Northwest Territories (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Norway. - /// - internal static string Norway { - get { - return ResourceManager.GetString("Norway", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nova Scotia (Canada). - /// - internal static string Nova_Scotia__Canada_ { - get { - return ResourceManager.GetString("Nova Scotia (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to November Time Zone. - /// - internal static string November_Time_Zone { - get { - return ResourceManager.GetString("November Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Novosibirsk (Russia). - /// - internal static string Novosibirsk__Russia_ { - get { - return ResourceManager.GetString("Novosibirsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Novosibirsk Time. - /// - internal static string Novosibirsk_Time { - get { - return ResourceManager.GetString("Novosibirsk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nunavut (central, Canada). - /// - internal static string Nunavut__central__Canada_ { - get { - return ResourceManager.GetString("Nunavut (central, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nunavut (east, Canada). - /// - internal static string Nunavut__east__Canada_ { - get { - return ResourceManager.GetString("Nunavut (east, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nunavut (west, Canada). - /// - internal static string Nunavut__west__Canada_ { - get { - return ResourceManager.GetString("Nunavut (west, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Offset. - /// - internal static string Offset { - get { - return ResourceManager.GetString("Offset", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ohio (United States). - /// - internal static string Ohio__United_States_ { - get { - return ResourceManager.GetString("Ohio (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Oklahoma, United States. - /// - internal static string Oklahoma__United_States { - get { - return ResourceManager.GetString("Oklahoma, United States", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Oman. - /// - internal static string Oman { - get { - return ResourceManager.GetString("Oman", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Omsk (Russia). - /// - internal static string Omsk__Russia_ { - get { - return ResourceManager.GetString("Omsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Omsk Time. - /// - internal static string Omsk_Time { - get { - return ResourceManager.GetString("Omsk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ontario (most, Canada). - /// - internal static string Ontario__most__Canada_ { - get { - return ResourceManager.GetString("Ontario (most, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ontario (west, Canada). - /// - internal static string Ontario__west__Canada_ { - get { - return ResourceManager.GetString("Ontario (west, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Oral Time. - /// - internal static string Oral_Time { - get { - return ResourceManager.GetString("Oral Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Oregon (most, United States). - /// - internal static string Oregon__most__United_States_ { - get { - return ResourceManager.GetString("Oregon (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Orenburg (Russia). - /// - internal static string Orenburg__Russia_ { - get { - return ResourceManager.GetString("Orenburg (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Oscar Time Zone. - /// - internal static string Oscar_Time_Zone { - get { - return ResourceManager.GetString("Oscar Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pacific Daylight Time. - /// - internal static string Pacific_Daylight_Time { - get { - return ResourceManager.GetString("Pacific Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pacific Standard Time. - /// - internal static string Pacific_Standard_Time { - get { - return ResourceManager.GetString("Pacific Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pacific Time Zone. - /// - internal static string Pacific_Time_Zone { - get { - return ResourceManager.GetString("Pacific Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pacific Time Zone. - /// - internal static string Pacific_Time_Zonev { - get { - return ResourceManager.GetString("Pacific Time Zonev", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pakistan. - /// - internal static string Pakistan { - get { - return ResourceManager.GetString("Pakistan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pakistan Standard Time. - /// - internal static string Pakistan_Standard_Time { - get { - return ResourceManager.GetString("Pakistan Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Palau. - /// - internal static string Palau { - get { - return ResourceManager.GetString("Palau", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Palau Time. - /// - internal static string Palau_Time { - get { - return ResourceManager.GetString("Palau Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Palestine. - /// - internal static string Palestine { - get { - return ResourceManager.GetString("Palestine", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Palmyra Atoll. - /// - internal static string Palmyra_Atoll { - get { - return ResourceManager.GetString("Palmyra Atoll", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Panama. - /// - internal static string Panama { - get { - return ResourceManager.GetString("Panama", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Papa Time Zone. - /// - internal static string Papa_Time_Zone { - get { - return ResourceManager.GetString("Papa Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Papua (Indonesia). - /// - internal static string Papua__Indonesia_ { - get { - return ResourceManager.GetString("Papua (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Papua New Guinea (most). - /// - internal static string Papua_New_Guinea__most_ { - get { - return ResourceManager.GetString("Papua New Guinea (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Papua New Guinea Time. - /// - internal static string Papua_New_Guinea_Time { - get { - return ResourceManager.GetString("Papua New Guinea Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Paraguay. - /// - internal static string Paraguay { - get { - return ResourceManager.GetString("Paraguay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Paraguay Summer Time. - /// - internal static string Paraguay_Summer_Time { - get { - return ResourceManager.GetString("Paraguay Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Paraguay Time. - /// - internal static string Paraguay_Time { - get { - return ResourceManager.GetString("Paraguay Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pennsylvania (United States). - /// - internal static string Pennsylvania__United_States_ { - get { - return ResourceManager.GetString("Pennsylvania (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Perm (Russia). - /// - internal static string Perm__Russia_ { - get { - return ResourceManager.GetString("Perm (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Peru. - /// - internal static string Peru { - get { - return ResourceManager.GetString("Peru", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Peru Time. - /// - internal static string Peru_Time { - get { - return ResourceManager.GetString("Peru Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Philippine Standard Time. - /// - internal static string Philippine_Standard_Time { - get { - return ResourceManager.GetString("Philippine Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Philippine Time. - /// - internal static string Philippine_Time { - get { - return ResourceManager.GetString("Philippine Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Philippines. - /// - internal static string Philippines { - get { - return ResourceManager.GetString("Philippines", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Phoenix Island Time. - /// - internal static string Phoenix_Island_Time { - get { - return ResourceManager.GetString("Phoenix Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Phoenix Islands (Kiribati). - /// - internal static string Phoenix_Islands__Kiribati_ { - get { - return ResourceManager.GetString("Phoenix Islands (Kiribati)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pitcairn Islands. - /// - internal static string Pitcairn_Islands { - get { - return ResourceManager.GetString("Pitcairn Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pitt Islands (New Zealand). - /// - internal static string Pitt_Islands__New_Zealand_ { - get { - return ResourceManager.GetString("Pitt Islands (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Gets the current time of a time zone. - /// - internal static string PluginDescription { - get { - return ResourceManager.GetString("PluginDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Time zone. - /// - internal static string PluginTitle { - get { - return ResourceManager.GetString("PluginTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pohnpei (Micronesia). - /// - internal static string Pohnpei__Micronesia_ { - get { - return ResourceManager.GetString("Pohnpei (Micronesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pohnpei Standard Time. - /// - internal static string Pohnpei_Standard_Time { - get { - return ResourceManager.GetString("Pohnpei Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Poland. - /// - internal static string Poland { - get { - return ResourceManager.GetString("Poland", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Portugal (most). - /// - internal static string Portugal__most_ { - get { - return ResourceManager.GetString("Portugal (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Primorsky (Russia). - /// - internal static string Primorsky__Russia_ { - get { - return ResourceManager.GetString("Primorsky (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prince Edward Island (Canada). - /// - internal static string Prince_Edward_Island__Canada_ { - get { - return ResourceManager.GetString("Prince Edward Island (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Prince Edward Islands (South Africa). - /// - internal static string Prince_Edward_Islands__South_Africa_ { - get { - return ResourceManager.GetString("Prince Edward Islands (South Africa)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Puerto Rico. - /// - internal static string Puerto_Rico { - get { - return ResourceManager.GetString("Puerto Rico", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Qatar. - /// - internal static string Qatar { - get { - return ResourceManager.GetString("Qatar", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quebec (east, Canada). - /// - internal static string Quebec__east__Canada_ { - get { - return ResourceManager.GetString("Quebec (east, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quebec (most, Canada). - /// - internal static string Quebec__most__Canada_ { - get { - return ResourceManager.GetString("Quebec (most, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quebec Time Zone. - /// - internal static string Quebec_Time_Zone { - get { - return ResourceManager.GetString("Quebec Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Queensland (Australia). - /// - internal static string Queensland__Australia_ { - get { - return ResourceManager.GetString("Queensland (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Quintana Roo (Mexico). - /// - internal static string Quintana_Roo__Mexico_ { - get { - return ResourceManager.GetString("Quintana Roo (Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rat Islands (Aleutian Islands, Alaska, United States). - /// - internal static string Rat_Islands__Aleutian_Islands__Alaska__United_States_ { - get { - return ResourceManager.GetString("Rat Islands (Aleutian Islands, Alaska, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Réunion. - /// - internal static string Réunion { - get { - return ResourceManager.GetString("Réunion", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Réunion Time. - /// - internal static string Réunion_Time { - get { - return ResourceManager.GetString("Réunion Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rhode Island (United States). - /// - internal static string Rhode_Island__United_States_ { - get { - return ResourceManager.GetString("Rhode Island (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Romania. - /// - internal static string Romania { - get { - return ResourceManager.GetString("Romania", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Romeo Time Zone. - /// - internal static string Romeo_Time_Zone { - get { - return ResourceManager.GetString("Romeo Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rondônia (Brazil). - /// - internal static string Rondônia__Brazil_ { - get { - return ResourceManager.GetString("Rondônia (Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Roraima (Brazil). - /// - internal static string Roraima__Brazil_ { - get { - return ResourceManager.GetString("Roraima (Brazil)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rothera Research Station Time. - /// - internal static string Rothera_Research_Station_Time { - get { - return ResourceManager.GetString("Rothera Research Station Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Russia (most of European part). - /// - internal static string Russia__most_of_European_part_ { - get { - return ResourceManager.GetString("Russia (most of European part)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Rwanda. - /// - internal static string Rwanda { - get { - return ResourceManager.GetString("Rwanda", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Barthélemy. - /// - internal static string Saint_Barthélemy { - get { - return ResourceManager.GetString("Saint Barthélemy", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Helena. - /// - internal static string Saint_Helena { - get { - return ResourceManager.GetString("Saint Helena", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Kitts and Nevis. - /// - internal static string Saint_Kitts_and_Nevis { - get { - return ResourceManager.GetString("Saint Kitts and Nevis", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Lucia. - /// - internal static string Saint_Lucia { - get { - return ResourceManager.GetString("Saint Lucia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Martin. - /// - internal static string Saint_Martin { - get { - return ResourceManager.GetString("Saint Martin", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Paul Island (French Southern and Antarctic Lands). - /// - internal static string Saint_Paul_Island__French_Southern_and_Antarctic_Lands_ { - get { - return ResourceManager.GetString("Saint Paul Island (French Southern and Antarctic Lands)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Pierre and Miquelon. - /// - internal static string Saint_Pierre_and_Miquelon { - get { - return ResourceManager.GetString("Saint Pierre and Miquelon", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Pierre and Miquelon Daylight Time. - /// - internal static string Saint_Pierre_and_Miquelon_Daylight_Time { - get { - return ResourceManager.GetString("Saint Pierre and Miquelon Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Pierre and Miquelon Standard Time. - /// - internal static string Saint_Pierre_and_Miquelon_Standard_Time { - get { - return ResourceManager.GetString("Saint Pierre and Miquelon Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saint Vincent and the Grenadines. - /// - internal static string Saint_Vincent_and_the_Grenadines { - get { - return ResourceManager.GetString("Saint Vincent and the Grenadines", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sakha (central-east, Russia). - /// - internal static string Sakha__central_east__Russia_ { - get { - return ResourceManager.GetString("Sakha (central-east, Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sakha (east, Russia). - /// - internal static string Sakha__east__Russia_ { - get { - return ResourceManager.GetString("Sakha (east, Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sakha (most, Russia). - /// - internal static string Sakha__most__Russia_ { - get { - return ResourceManager.GetString("Sakha (most, Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sakhalin (Russia). - /// - internal static string Sakhalin__Russia_ { - get { - return ResourceManager.GetString("Sakhalin (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sakhalin Island Time. - /// - internal static string Sakhalin_Island_Time { - get { - return ResourceManager.GetString("Sakhalin Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Samara (Russia). - /// - internal static string Samara__Russia_ { - get { - return ResourceManager.GetString("Samara (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Samara Time. - /// - internal static string Samara_Time { - get { - return ResourceManager.GetString("Samara Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Samoa. - /// - internal static string Samoa { - get { - return ResourceManager.GetString("Samoa", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Samoa Daylight Time. - /// - internal static string Samoa_Daylight_Time { - get { - return ResourceManager.GetString("Samoa Daylight Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Samoa Standard Time. - /// - internal static string Samoa_Standard_Time { - get { - return ResourceManager.GetString("Samoa Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Samoa Time Zone. - /// - internal static string Samoa_Time_Zone { - get { - return ResourceManager.GetString("Samoa Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to San Marino. - /// - internal static string San_Marino { - get { - return ResourceManager.GetString("San Marino", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to São Tomé and Príncipe. - /// - internal static string São_Tomé_and_Príncipe { - get { - return ResourceManager.GetString("São Tomé and Príncipe", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saratov (Russia). - /// - internal static string Saratov__Russia_ { - get { - return ResourceManager.GetString("Saratov (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saskatchewan (most, Canada). - /// - internal static string Saskatchewan__most__Canada_ { - get { - return ResourceManager.GetString("Saskatchewan (most, Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Saudi Arabia. - /// - internal static string Saudi_Arabia { - get { - return ResourceManager.GetString("Saudi Arabia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Scattered Islands (French Southern and Antarctic Lands). - /// - internal static string Scattered_Islands__French_Southern_and_Antarctic_Lands_ { - get { - return ResourceManager.GetString("Scattered Islands (French Southern and Antarctic Lands)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Senegal. - /// - internal static string Senegal { - get { - return ResourceManager.GetString("Senegal", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Serbia. - /// - internal static string Serbia { - get { - return ResourceManager.GetString("Serbia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Seychelles. - /// - internal static string Seychelles { - get { - return ResourceManager.GetString("Seychelles", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Seychelles Time. - /// - internal static string Seychelles_Time { - get { - return ResourceManager.GetString("Seychelles Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Shortcuts. - /// - internal static string Shortcuts { - get { - return ResourceManager.GetString("Shortcuts", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Showa Station Time. - /// - internal static string Showa_Station_Time { - get { - return ResourceManager.GetString("Showa Station Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show military time zone names. - /// - internal static string ShowMilitaryTimeZoneNames { - get { - return ResourceManager.GetString("ShowMilitaryTimeZoneNames", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show time names. - /// - internal static string ShowTimeNames { - get { - return ResourceManager.GetString("ShowTimeNames", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show time zone names. - /// - internal static string ShowTimeZoneNames { - get { - return ResourceManager.GetString("ShowTimeZoneNames", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sierra Leone. - /// - internal static string Sierra_Leone { - get { - return ResourceManager.GetString("Sierra Leone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sierra Time Zone. - /// - internal static string Sierra_Time_Zone { - get { - return ResourceManager.GetString("Sierra Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sinaloa (Mexico). - /// - internal static string Sinaloa__Mexico_ { - get { - return ResourceManager.GetString("Sinaloa (Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Singapore. - /// - internal static string Singapore { - get { - return ResourceManager.GetString("Singapore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Singapore Standard Time. - /// - internal static string Singapore_Standard_Time { - get { - return ResourceManager.GetString("Singapore Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Singapore Time. - /// - internal static string Singapore_Time { - get { - return ResourceManager.GetString("Singapore Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sint Maarten. - /// - internal static string Sint_Maarten { - get { - return ResourceManager.GetString("Sint Maarten", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Slovakia. - /// - internal static string Slovakia { - get { - return ResourceManager.GetString("Slovakia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Slovenia. - /// - internal static string Slovenia { - get { - return ResourceManager.GetString("Slovenia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Solomon Islands. - /// - internal static string Solomon_Islands { - get { - return ResourceManager.GetString("Solomon Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Solomon Islands Time. - /// - internal static string Solomon_Islands_Time { - get { - return ResourceManager.GetString("Solomon Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Somalia. - /// - internal static string Somalia { - get { - return ResourceManager.GetString("Somalia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Somaliland. - /// - internal static string Somaliland { - get { - return ResourceManager.GetString("Somaliland", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sonora (Mexico). - /// - internal static string Sonora__Mexico_ { - get { - return ResourceManager.GetString("Sonora (Mexico)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Africa (most). - /// - internal static string South_Africa__most_ { - get { - return ResourceManager.GetString("South Africa (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South African Standard Time. - /// - internal static string South_African_Standard_Time { - get { - return ResourceManager.GetString("South African Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Australia (Australia). - /// - internal static string South_Australia__Australia_ { - get { - return ResourceManager.GetString("South Australia (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Carolina (United States). - /// - internal static string South_Carolina__United_States_ { - get { - return ResourceManager.GetString("South Carolina (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Dakota (most, United States). - /// - internal static string South_Dakota__most__United_States_ { - get { - return ResourceManager.GetString("South Dakota (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South East Island (New Zealand). - /// - internal static string South_East_Island__New_Zealand_ { - get { - return ResourceManager.GetString("South East Island (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Georgia and the South Sandwich Islands. - /// - internal static string South_Georgia_and_the_South_Sandwich_Islands { - get { - return ResourceManager.GetString("South Georgia and the South Sandwich Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Georgia and the South Sandwich Islands Time. - /// - internal static string South_Georgia_and_the_South_Sandwich_Islands_Time { - get { - return ResourceManager.GetString("South Georgia and the South Sandwich Islands Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Kalimantan (Indonesia). - /// - internal static string South_Kalimantan__Indonesia_ { - get { - return ResourceManager.GetString("South Kalimantan (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Korea. - /// - internal static string South_Korea { - get { - return ResourceManager.GetString("South Korea", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Ossetia. - /// - internal static string South_Ossetia { - get { - return ResourceManager.GetString("South Ossetia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to South Sudan. - /// - internal static string South_Sudan { - get { - return ResourceManager.GetString("South Sudan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Southampton Island (Canada). - /// - internal static string Southampton_Island__Canada_ { - get { - return ResourceManager.GetString("Southampton Island (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Spain (most). - /// - internal static string Spain__most_ { - get { - return ResourceManager.GetString("Spain (most)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Srednekolymsk Time. - /// - internal static string Srednekolymsk_Time { - get { - return ResourceManager.GetString("Srednekolymsk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sri Lanka. - /// - internal static string Sri_Lanka { - get { - return ResourceManager.GetString("Sri Lanka", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sri Lanka Standard Time. - /// - internal static string Sri_Lanka_Standard_Time { - get { - return ResourceManager.GetString("Sri Lanka Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Standard Time. - /// - internal static string StandardTime { - get { - return ResourceManager.GetString("StandardTime", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ST. - /// - internal static string StandardTimeShortcut { - get { - return ResourceManager.GetString("StandardTimeShortcut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Star Keys (New Zealand). - /// - internal static string Star_Keys__New_Zealand_ { - get { - return ResourceManager.GetString("Star Keys (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sud-Ubangi (Democratic Republic of the Congo). - /// - internal static string Sud_Ubangi__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Sud-Ubangi (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sudan. - /// - internal static string Sudan { - get { - return ResourceManager.GetString("Sudan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sulawesi (Indonesia). - /// - internal static string Sulawesi__Indonesia_ { - get { - return ResourceManager.GetString("Sulawesi (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sumatra (Indonesia). - /// - internal static string Sumatra__Indonesia_ { - get { - return ResourceManager.GetString("Sumatra (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to SuriName. - /// - internal static string SuriName { - get { - return ResourceManager.GetString("SuriName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Suriname Time. - /// - internal static string Suriname_Time { - get { - return ResourceManager.GetString("Suriname Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sverdlovsk (Russia). - /// - internal static string Sverdlovsk__Russia_ { - get { - return ResourceManager.GetString("Sverdlovsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sweden. - /// - internal static string Sweden { - get { - return ResourceManager.GetString("Sweden", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Switzerland. - /// - internal static string Switzerland { - get { - return ResourceManager.GetString("Switzerland", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Syria. - /// - internal static string Syria { - get { - return ResourceManager.GetString("Syria", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tahiti Time. - /// - internal static string Tahiti_Time { - get { - return ResourceManager.GetString("Tahiti Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Taiwan. - /// - internal static string Taiwan { - get { - return ResourceManager.GetString("Taiwan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tajikistan. - /// - internal static string Tajikistan { - get { - return ResourceManager.GetString("Tajikistan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tajikistan Time. - /// - internal static string Tajikistan_Time { - get { - return ResourceManager.GetString("Tajikistan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tango Time Zone. - /// - internal static string Tango_Time_Zone { - get { - return ResourceManager.GetString("Tango Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tanzania. - /// - internal static string Tanzania { - get { - return ResourceManager.GetString("Tanzania", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tasmania (Australia). - /// - internal static string Tasmania__Australia_ { - get { - return ResourceManager.GetString("Tasmania (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tennessee (most, United States). - /// - internal static string Tennessee__most__United_States_ { - get { - return ResourceManager.GetString("Tennessee (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Texas (most, United States). - /// - internal static string Texas__most__United_States_ { - get { - return ResourceManager.GetString("Texas (most, United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Thailand. - /// - internal static string Thailand { - get { - return ResourceManager.GetString("Thailand", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Thailand Standard Time. - /// - internal static string Thailand_Standard_Time { - get { - return ResourceManager.GetString("Thailand Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The Fort (New Zealand). - /// - internal static string The_Fort__New_Zealand_ { - get { - return ResourceManager.GetString("The Fort (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The Sisters (New Zealand). - /// - internal static string The_Sisters__New_Zealand_ { - get { - return ResourceManager.GetString("The Sisters (New Zealand)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Thule Air Base (Greenland). - /// - internal static string Thule_Air_Base__Greenland_ { - get { - return ResourceManager.GetString("Thule Air Base (Greenland)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Time. - /// - internal static string Time { - get { - return ResourceManager.GetString("Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to T. - /// - internal static string TimeShortcut { - get { - return ResourceManager.GetString("TimeShortcut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Time Zone. - /// - internal static string TimeZone { - get { - return ResourceManager.GetString("TimeZone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to TZ. - /// - internal static string TimeZoneShortcut { - get { - return ResourceManager.GetString("TimeZoneShortcut", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Timor Leste Time. - /// - internal static string Timor_Leste_Time { - get { - return ResourceManager.GetString("Timor Leste Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Togo. - /// - internal static string Togo { - get { - return ResourceManager.GetString("Togo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tokelau. - /// - internal static string Tokelau { - get { - return ResourceManager.GetString("Tokelau", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tokelau Time. - /// - internal static string Tokelau_Time { - get { - return ResourceManager.GetString("Tokelau Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tomsk (Russia). - /// - internal static string Tomsk__Russia_ { - get { - return ResourceManager.GetString("Tomsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tonga. - /// - internal static string Tonga { - get { - return ResourceManager.GetString("Tonga", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tonga Time. - /// - internal static string Tonga_Time { - get { - return ResourceManager.GetString("Tonga Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Transnistria. - /// - internal static string Transnistria { - get { - return ResourceManager.GetString("Transnistria", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Trinidad and Tobago. - /// - internal static string Trinidad_and_Tobago { - get { - return ResourceManager.GetString("Trinidad and Tobago", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tshuapa (Democratic Republic of the Congo). - /// - internal static string Tshuapa__Democratic_Republic_of_the_Congo_ { - get { - return ResourceManager.GetString("Tshuapa (Democratic Republic of the Congo)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tunisia. - /// - internal static string Tunisia { - get { - return ResourceManager.GetString("Tunisia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Turkey. - /// - internal static string Turkey { - get { - return ResourceManager.GetString("Turkey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Turkey Time. - /// - internal static string Turkey_Time { - get { - return ResourceManager.GetString("Turkey Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Turkmenistan. - /// - internal static string Turkmenistan { - get { - return ResourceManager.GetString("Turkmenistan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Turkmenistan Time. - /// - internal static string Turkmenistan_Time { - get { - return ResourceManager.GetString("Turkmenistan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Turks and Caicos Islands. - /// - internal static string Turks_and_Caicos_Islands { - get { - return ResourceManager.GetString("Turks and Caicos Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tuva (Russia). - /// - internal static string Tuva__Russia_ { - get { - return ResourceManager.GetString("Tuva (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tuvalu. - /// - internal static string Tuvalu { - get { - return ResourceManager.GetString("Tuvalu", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tuvalu Time. - /// - internal static string Tuvalu_Time { - get { - return ResourceManager.GetString("Tuvalu Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Tyumen (Russia). - /// - internal static string Tyumen__Russia_ { - get { - return ResourceManager.GetString("Tyumen (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to U.S. Virgin Islands. - /// - internal static string U_S__Virgin_Islands { - get { - return ResourceManager.GetString("U.S. Virgin Islands", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Udmurtia (Russia). - /// - internal static string Udmurtia__Russia_ { - get { - return ResourceManager.GetString("Udmurtia (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uganda. - /// - internal static string Uganda { - get { - return ResourceManager.GetString("Uganda", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ulaanbaatar Standard Time. - /// - internal static string Ulaanbaatar_Standard_Time { - get { - return ResourceManager.GetString("Ulaanbaatar Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ulaanbaatar Summer Time. - /// - internal static string Ulaanbaatar_Summer_Time { - get { - return ResourceManager.GetString("Ulaanbaatar Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Ulyanovsk (Russia). - /// - internal static string Ulyanovsk__Russia_ { - get { - return ResourceManager.GetString("Ulyanovsk (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uniform Time Zone. - /// - internal static string Uniform_Time_Zone { - get { - return ResourceManager.GetString("Uniform Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to United Arab Emirates. - /// - internal static string United_Arab_Emirates { - get { - return ResourceManager.GetString("United Arab Emirates", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to United Arab Emirates Standard Time. - /// - internal static string United_Arab_Emirates_Standard_Time { - get { - return ResourceManager.GetString("United Arab Emirates Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to United Kingdom. - /// - internal static string United_Kingdom { - get { - return ResourceManager.GetString("United Kingdom", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uruguay. - /// - internal static string Uruguay { - get { - return ResourceManager.GetString("Uruguay", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uruguay Standard Time. - /// - internal static string Uruguay_Standard_Time { - get { - return ResourceManager.GetString("Uruguay Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uruguay Summer Time. - /// - internal static string Uruguay_Summer_Time { - get { - return ResourceManager.GetString("Uruguay Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Use daylight saving time. - /// - internal static string UseDst { - get { - return ResourceManager.GetString("UseDst", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Utah (United States). - /// - internal static string Utah__United_States_ { - get { - return ResourceManager.GetString("Utah (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UTC. - /// - internal static string UTC { - get { - return ResourceManager.GetString("UTC", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uvs (Mongolia). - /// - internal static string Uvs__Mongolia_ { - get { - return ResourceManager.GetString("Uvs (Mongolia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uzbekistan. - /// - internal static string Uzbekistan { - get { - return ResourceManager.GetString("Uzbekistan", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Uzbekistan Time. - /// - internal static string Uzbekistan_Time { - get { - return ResourceManager.GetString("Uzbekistan Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vanuatu. - /// - internal static string Vanuatu { - get { - return ResourceManager.GetString("Vanuatu", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vanuatu Time. - /// - internal static string Vanuatu_Time { - get { - return ResourceManager.GetString("Vanuatu Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vatican City. - /// - internal static string Vatican_City { - get { - return ResourceManager.GetString("Vatican City", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Venezuela. - /// - internal static string Venezuela { - get { - return ResourceManager.GetString("Venezuela", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Venezuelan Standard Time. - /// - internal static string Venezuelan_Standard_Time { - get { - return ResourceManager.GetString("Venezuelan Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vermont (United States). - /// - internal static string Vermont__United_States_ { - get { - return ResourceManager.GetString("Vermont (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Victor Time Zone. - /// - internal static string Victor_Time_Zone { - get { - return ResourceManager.GetString("Victor Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Victoria (Australia). - /// - internal static string Victoria__Australia_ { - get { - return ResourceManager.GetString("Victoria (Australia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vietnam. - /// - internal static string Vietnam { - get { - return ResourceManager.GetString("Vietnam", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Virginia (United States). - /// - internal static string Virginia__United_States_ { - get { - return ResourceManager.GetString("Virginia (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vladivostok Time. - /// - internal static string Vladivostok_Time { - get { - return ResourceManager.GetString("Vladivostok Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Volgograd Time. - /// - internal static string Volgograd_Time { - get { - return ResourceManager.GetString("Volgograd Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Vostok Station Time. - /// - internal static string Vostok_Station_Time { - get { - return ResourceManager.GetString("Vostok Station Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wake Island. - /// - internal static string Wake_Island { - get { - return ResourceManager.GetString("Wake Island", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wake Island Time. - /// - internal static string Wake_Island_Time { - get { - return ResourceManager.GetString("Wake Island Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wallis and Futuna. - /// - internal static string Wallis_and_Futuna { - get { - return ResourceManager.GetString("Wallis and Futuna", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Washington (United States). - /// - internal static string Washington__United_States_ { - get { - return ResourceManager.GetString("Washington (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Africa Summer Time. - /// - internal static string West_Africa_Summer_Time { - get { - return ResourceManager.GetString("West Africa Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Africa Time. - /// - internal static string West_Africa_Time { - get { - return ResourceManager.GetString("West Africa Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Greenland Summer Time. - /// - internal static string West_Greenland_Summer_Time { - get { - return ResourceManager.GetString("West Greenland Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Greenland Time. - /// - internal static string West_Greenland_Time { - get { - return ResourceManager.GetString("West Greenland Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Kalimantan (Indonesia). - /// - internal static string West_Kalimantan__Indonesia_ { - get { - return ResourceManager.GetString("West Kalimantan (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Kazakhstan (Kazakhstan). - /// - internal static string West_Kazakhstan__Kazakhstan_ { - get { - return ResourceManager.GetString("West Kazakhstan (Kazakhstan)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Nusa Tenggara (Indonesia). - /// - internal static string West_Nusa_Tenggara__Indonesia_ { - get { - return ResourceManager.GetString("West Nusa Tenggara (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Papua (Indonesia). - /// - internal static string West_Papua__Indonesia_ { - get { - return ResourceManager.GetString("West Papua (Indonesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to West Virginia (United States). - /// - internal static string West_Virginia__United_States_ { - get { - return ResourceManager.GetString("West Virginia (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Weste. - /// - internal static string Weste { - get { - return ResourceManager.GetString("Weste", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Western Australia Standard Time. - /// - internal static string Western_Australia_Standard_Time { - get { - return ResourceManager.GetString("Western Australia Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Western European Summer Time. - /// - internal static string Western_European_Summer_Time { - get { - return ResourceManager.GetString("Western European Summer Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Western European Time. - /// - internal static string Western_European_Time { - get { - return ResourceManager.GetString("Western European Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Western Indonesian Time. - /// - internal static string Western_Indonesian_Time { - get { - return ResourceManager.GetString("Western Indonesian Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Western Standard Time. - /// - internal static string Western_Standard_Time { - get { - return ResourceManager.GetString("Western Standard Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Whiskey Time Zone. - /// - internal static string Whiskey_Time_Zone { - get { - return ResourceManager.GetString("Whiskey Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wisconsin (United States). - /// - internal static string Wisconsin__United_States_ { - get { - return ResourceManager.GetString("Wisconsin (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Wyoming (United States). - /// - internal static string Wyoming__United_States_ { - get { - return ResourceManager.GetString("Wyoming (United States)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to X-ray Time Zone. - /// - internal static string X_ray_Time_Zone { - get { - return ResourceManager.GetString("X-ray Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yakutsk Time. - /// - internal static string Yakutsk_Time { - get { - return ResourceManager.GetString("Yakutsk Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yamalia (Russia). - /// - internal static string Yamalia__Russia_ { - get { - return ResourceManager.GetString("Yamalia (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yankee Time Zone. - /// - internal static string Yankee_Time_Zone { - get { - return ResourceManager.GetString("Yankee Time Zone", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yap (Micronesia). - /// - internal static string Yap__Micronesia_ { - get { - return ResourceManager.GetString("Yap (Micronesia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yekaterinburg Time. - /// - internal static string Yekaterinburg_Time { - get { - return ResourceManager.GetString("Yekaterinburg Time", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yemen. - /// - internal static string Yemen { - get { - return ResourceManager.GetString("Yemen", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yes. - /// - internal static string Yes { - get { - return ResourceManager.GetString("Yes", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Yukon (Canada). - /// - internal static string Yukon__Canada_ { - get { - return ResourceManager.GetString("Yukon (Canada)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Zabaykalsky (Russia). - /// - internal static string Zabaykalsky__Russia_ { - get { - return ResourceManager.GetString("Zabaykalsky (Russia)", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Zambia. - /// - internal static string Zambia { - get { - return ResourceManager.GetString("Zambia", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Zimbabwe. - /// - internal static string Zimbabwe { - get { - return ResourceManager.GetString("Zimbabwe", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Zulu Time Zone. - /// - internal static string Zulu_Time_Zone { - get { - return ResourceManager.GetString("Zulu Time Zone", resourceCulture); - } - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx deleted file mode 100644 index 61d12501d8..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/Properties/Resources.resx +++ /dev/null @@ -1,2349 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Abkhazia - - - Acre (Brazil) - - - Acre Time - - - Afghanistan - - - Afghanistan Time - - - Akrotiri and Dhekelia - - - Aktobe (Kazakhstan) - - - Alabama (United States) - - - Alaska (most, United States) - - - Alaska Daylight Time - - - Alaska Standard Time - - - Alaska Time Zone - - - Albania - - - Alberta (Canada) - - - Algeria - - - Alma-Ata Time - - - Alpha Time Zone - Military time zone name - - - Altai Krai (Russia) - - - Altai Republic (Russia) - - - Amazon Summer Time - - - Amazon Time - - - Amazonas (most, Brazil) - - - American Samoa - - - Amsterdam Island (French Southern and Antarctic Lands) - - - Amur (Russia) - - - Anadyr Time - - - Andorra - - - Andreanof Islands (United States) - - - Angola - - - Anguilla - - - Antigua and Barbuda - - - Aqtobe Time - - - Arabia Standard Time - - - Argentina - - - Argentina Time - - - Arizona (most, United States) - - - Arkansas (United States) - - - Armenia - - - Armenia Time - - - Artsakh - - - Aruba - - - Ascension and Tristan da Cunha - - - ASEAN Common Time - - - Astrakhan (Russia) - - - Atikokan (Canada) - - - Atlantic Daylight Time - - - Atlantic Standard Time - - - Atlantic Time Zone - - - Atyrau (Kazakhstan) - - - Australia: Western Australia (most) - - - Australian Capital Territory (Australia) - - - Australian Central Daylight Saving Time - - - Australian Central Standard Time - - - Australian Central Western Standard Time - - - Australian Eastern Daylight Saving Time - - - Australian Eastern Standard Time - - - Australian Eastern Time - - - Australian Western Standard Time - - - Austria - - - Azerbaijan - - - Azerbaijan Time - - - Azores (Portugal) - - - Azores Standard Time - - - Azores Summer Time - - - Bahamas - - - Bahrain - - - Baikonur (Kazakhstan) - - - Baja California (Mexico) - - - Baja California Sur (Mexico) - - - Baker Island - - - Baker Island Time - - - Bali (Indonesia) - - - Bangladesh - - - Bangladesh Standard Time - - - Barbados - - - Bashkortostan (Russia) - - - Bayan-Ölgii (Mongolia) - - - Belarus - - - Belgium - - - Belize - - - Benin - - - Bermuda - - - Bhutan - - - Bhutan Time - - - Bolivia - - - Bolivia Time - - - Border Village (Australia) - - - Bosnia and Herzegovina - - - Botswana - - - Bougainville (Papua New Guinea) - - - Bougainville Standard Time - - - Brasília Summer Time - - - Brasília Time - - - Bravo Time Zone - Military time zone name - - - Brazil (most) - - - British Columbia (most, Canada) - - - British Columbia (northeast, Canada) - - - British Columbia (southeast, Canada) - - - British Indian Ocean Territory - - - British Indian Ocean Time - - - British Summer Time - - - British Virgin Islands - - - Brunei - - - Brunei Time - - - Bulgaria - - - Burkina Faso - - - Burundi - - - Buryatia (Russia) - - - Caiguna (Australia) - - - California (United States) - - - Cambodia - - - Cameroon - - - Canary Islands - - - Cape Verde - - - Cape Verde Time - - - Caribbean Netherlands - - - Cayman Islands - - - Central Africa Time - - - Central African Republic - - - Central Daylight Time - - - Central European Summer Time - - - Central European Time - - - Central Indonesian Time - - - Central Kalimantan (Indonesia) - - - Central Standard Time - - - Central Time Zone - - - Central Western Standard Time - - - Chad - - - Chamorro Standard Time - - - Chamorro Time Zone - - - Charlie Time Zone - Military time zone name - - - Chatham Daylight Time - - - Chatham Islands (New Zealand) - - - Chatham Standard Time - - - Chelyabinsk (Russia) - - - Chihuahua (Mexico) - - - Chile (most) - - - Chile Summer Time - - - China - - - China Standard Time - - - Choibalsan Standard Time - - - Choibalsan Summer Time - - - Christmas Island - - - Christmas Island Time - - - Chukotka (Russia) - - - Chuuk (Micronesia) - - - Chuuk Time - - - Clipperton Island - - - Clipperton Island Standard Time - - - Cocklebiddy (Australia) - - - Cocos Islands - - - Cocos Islands Time - - - Colombia - - - Colombia Summer Time - - - Colombia Time - - - Colorado (United States) - - - Comoros - - - Congo - - - Connecticut (United States) - - - Cook Islands - - - Coordinated Universal Time - - - Copy time - - - Costa Rica - - - Countries - - - Countries with daylight saving time - - - Crimea - - - Croatia - - - Crozet Islands (French Southern and Antarctic Lands) - - - Cuba - - - Cuba Daylight Time - - - Cuba Standard Time - - - Curaçao - - - Cyprus - - - Czech Republic - - - Danmarkshavn - - - Davis Time - - - Daylight Time - - - DT - Short for "Daylight Time" - - - Delaware (United States) - - - Delta Time Zone - Military time zone name - - - Democratic Republic of the Congo (most) - - - Denmark - - - District of Columbia (United States) - - - Djibouti - - - Dominica - - - Dominican Republic - - - Donetsk PR - - - Dumont d'Urville Time - - - East Africa Time - - - East Kalimantan (Indonesia) - - - East Nusa Tenggara (Indonesia) - - - East Timor - - - Easter Island (Chile) - - - Easter Island Standard Time - - - Easter Island Summer Time - - - Eastern Caribbean Time - - - Eastern Daylight Time - - - Eastern European Summer Time - - - Eastern European Time - - - Eastern Greenland Summer Time - - - Eastern Greenland Time - - - Eastern Indonesian Time - - - Eastern Standard Time - - - Eastern Time Zone - - - Echo Time Zone - Military time zone name - - - Ecuador (most) - - - Ecuador Time - - - Ecuador: Galápagos - - - Egypt - - - El Salvador - - - Equatorial Guinea - - - Eritrea - - - Estonia - - - Eswatini - - - Ethiopia - - - Eucla (Australia) - - - Falkland Islands - - - Falkland Islands Summer Time - - - Falkland Islands Time - - - Faroe Islands - - - Fernando de Noronha (Brazil) - - - Fernando de Noronha Time - - - Fiji - - - Fiji Time - - - Finland - - - Florida (most, United States) - - - Forty-Fours (New Zealand) - - - Foxtrot Time Zone - Military time zone name - - - France (metropolitan) - - - French Guiana - - - French Guiana Time - - - French Polynesia (most) - - - French Southern and Antarctic Time - - - Further-eastern European Time - - - Gabon - - - Galápagos Time - - - Gambia - - - Gambier Islands (French Polynesia) - - - Gambier Islands Time - - - Georgia - - - Georgia (United States) - - - Georgia Standard Time - - - Germany - - - Ghana - - - Gibraltar - - - Gilbert Island Time - - - Gilbert Islands (Kiribati) - - - Golf Time Zone - Military time zone name - - - Greece - - - Greenland (most) - - - Greenwich Mean Time - - - Grenada - - - Guadeloupe - - - Guam - - - Guatemala - - - Guernsey - - - Guinea - - - Guinea-Bissau - - - Gulf Standard Time - - - Guyana - - - Guyana Time - - - Haiti - - - Hawaii (United States) - - - Hawaii-Aleutian Daylight Time - - - Hawaii-Aleutian Standard Time - - - Hawaii-Aleutian Time Zone - - - Heard and McDonald Islands Time - - - Heard Island and McDonald Islands - - - Heure Avancée d'Europe Centrale - - - Honduras - - - Hong Kong - - - Hong Kong Time - - - Hotel Time Zone - Military time zone name - - - Hovd Time - - - Howland Island - - - Hungary - - - Iceland - - - Idaho (most, United States) - - - Idaho (north, United States) - - - Illinois (United States) - - - India - - - India Time Zone - Military time zone name - - - Indian Ocean Time - - - Indian Standard Time - - - Indiana (most, United States) - - - Indochina Time - - - International Date Line West time zone - - - Iowa (United States) - - - Iran - - - Iran Daylight Time - - - Iran Standard Time - - - Iraq - - - Ireland - - - Irish Standard Time - - - Irkutsk (Russia) - - - Irkutsk Time - - - Islands of Four Mountains (United States) - - - Isle of Man - - - Israel - - - Israel Daylight Time - - - Israel Standard Time - - - Italy - - - Ittoqqortoormiit (Greenland) - - - Ivory Coast - - - Jamaica - - - Japan - - - Japan Standard Time - - - Jarvis Island - - - Java (Indonesia) - - - Jersey - - - Jervis Bay Territory (Australia) - - - Jewish (Russia) - - - Johnston Atoll - - - Jordan - - - Kaliningrad (Russia) - - - Kaliningrad Time - - - Kamchatka (Russia) - - - Kamchatka Time - - - Kansas (most, United States) - - - Kazakhstan (most) - - - Kemerovo (Russia) - - - Kentucky (most, United States) - - - Kenya - - - Kerguelen Islands (French Southern and Antarctic Lands) - - - Khabarovsk (Russia) - - - Khakassia (Russia) - - - Khanty-Mansi (Russia) - - - Khovd (Mongolia) - - - Kilo Time Zone - Military time zone name - - - Kingman Reef - - - Kinshasa (Democratic Republic of the Congo) - - - Kongo Central (Democratic Republic of the Congo) - - - Korea Standard Time - - - Kosovo - - - Kosrae (Micronesia) - - - Kosrae Time - - - Krasnoyarsk (Russia) - - - Krasnoyarsk Time - - - Kurgan (Russia) - - - Kuwait - - - Kwango (Democratic Republic of the Congo) - - - Kwilu (Democratic Republic of the Congo) - - - Kyrgyzstan - - - Kyrgyzstan Time - - - Kyzylorda (Kazakhstan) - - - Labrador (most, Canada) - - - Labrador (southeast, Canada) - - - Laos - - - Latvia - - - Lebanon - - - Lesotho - - - Liberia - - - Libya - - - Liechtenstein - - - Lima Time Zone - Military time zone name - - - Line Islands (Kiribati) - - - Line Islands Time - - - Lithuania - - - Little Mangere Island (New Zealand) - - - Lord Howe Island (Australia) - - - Lord Howe Standard Time - - - Lord Howe Summer Time - - - Louisiana (United States) - - - Luhansk PR - - - Luxembourg - - - Macau - - - Macquarie Island Station Time - - - Madagascar - - - Madura (Australia) - - - Magadan (Russia) - - - Magadan Time - - - Magallanes (Chile) - - - Mai-Ndombe (Democratic Republic of the Congo) - - - Maine (United States) - - - Malawi - - - Malaysia - - - Malaysia Standard Time - - - Malaysia Time - - - Maldives - - - Maldives Time - - - Mali - - - Malta - - - Maluku (Indonesia) - - - Mangystau (Kazakhstan) - - - Manitoba (Canada) - - - Marquesas Islands (French Polynesia) - - - Marquesas Islands Time - - - Marshall Islands - - - Marshall Islands Time - - - Martinique - - - Maryland (United States) - - - Massachusetts - - - Mato Grosso (Brazil) - - - Mato Grosso do Sul (Brazil) - - - Mauritania - - - Mauritius - - - Mauritius Time - - - Mawson Station Time - - - Mayotte - - - Mexico (most) - - - Michigan (most, United States) - - - Middle European Summer Time - - - Middle European Time - - - Midway Atoll - - - Mike Time Zone - Military time zone name - - - Minnesota (United States) - - - Mishkeegogamang (Canada) - - - Mississippi (United States) - - - Missouri (United States) - - - Moldova - - - Monaco - - - Mongala (Democratic Republic of the Congo) - - - Mongolia (most) - - - Montana (United States) - - - Montenegro - - - Montserrat - - - Morocco - - - Moscow Time - - - Mountain Daylight Time - - - Mountain Standard Time - - - Mountain Time Zone - - - Mozambique - - - Mundrabilla (Australia) - - - Myanmar - - - Myanmar Standard Time - - - Names - - - Namibia - - - Nauru - - - Navassa Island - - - Nayarit (most, Mexico) - - - Near Islands (United States) - - - Nebraska (most, United States) - - - Nepal - - - Nepal Standard Time - - - Netherlands (European) - - - Nevada (United States) - - - New Brunswick (Canada) - - - New Caledonia - - - New Caledonia Time - - - New Hampshire (United States) - - - New Jersey (United States) - - - New Mexico (United States) - - - New South Wales (most, Australia) - - - New York (United States) - - - New Zealand - - - New Zealand Daylight Time - - - New Zealand Standard Time - - - Newfoundland (Canada) - - - Newfoundland Daylight Time - - - Newfoundland Standard Time - - - Newfoundland Time Zone - - - Nicaragua - - - Niger - - - Nigeria - - - Niue - - - Niue Time - - - No - - - Nord-Ubangi (Democratic Republic of the Congo) - - - Norfolk Island - - - Norfolk Island Time - - - North Carolina (United States) - - - North Dakota (most, United States) - - - North Kalimantan (Indonesia) - - - North Korea - - - North Macedonia - - - North Maluku (Indonesia) - - - Northern Cyprus - - - Northern Mariana Islands - - - Northern Territory - - - Northwest Territories (Canada) - - - Norway - - - Nova Scotia (Canada) - - - November Time Zone - Military time zone name - - - Novosibirsk (Russia) - - - Novosibirsk Time - - - Nunavut (central, Canada) - - - Nunavut (east, Canada) - - - Nunavut (west, Canada) - - - Offset - - - Ohio (United States) - - - Oklahoma, United States - - - Oman - - - Omsk (Russia) - - - Omsk Time - - - Ontario (most, Canada) - - - Ontario (west, Canada) - - - Oral Time - - - Oregon (most, United States) - - - Orenburg (Russia) - - - Oscar Time Zone - Military time zone name - - - Pacific Daylight Time - - - Pacific Standard Time - - - Pacific Time Zone - - - Pacific Time Zone - - - Pakistan - - - Pakistan Standard Time - - - Palau - - - Palau Time - - - Palestine - - - Palmyra Atoll - - - Panama - - - Papa Time Zone - Military time zone name - - - Papua (Indonesia) - - - Papua New Guinea (most) - - - Papua New Guinea Time - - - Paraguay - - - Paraguay Summer Time - - - Paraguay Time - - - Pennsylvania (United States) - - - Perm (Russia) - - - Peru - - - Peru Time - - - Philippine Standard Time - - - Philippine Time - - - Philippines - - - Phoenix Island Time - - - Phoenix Islands (Kiribati) - - - Pitcairn Islands - - - Pitt Islands (New Zealand) - - - Gets the current time of a time zone - - - Time zone - - - Pohnpei (Micronesia) - - - Pohnpei Standard Time - - - Poland - - - Portugal (most) - - - Primorsky (Russia) - - - Prince Edward Island (Canada) - - - Prince Edward Islands (South Africa) - - - Puerto Rico - - - Qatar - - - Quebec (east, Canada) - - - Quebec (most, Canada) - - - Quebec Time Zone - Military time zone name - - - Queensland (Australia) - - - Quintana Roo (Mexico) - - - Rat Islands (Aleutian Islands, Alaska, United States) - - - Rhode Island (United States) - - - Romania - - - Romeo Time Zone - Military time zone name - - - Rondônia (Brazil) - - - Roraima (Brazil) - - - Rothera Research Station Time - - - Russia (most of European part) - - - Rwanda - - - Réunion - - - Réunion Time - - - Saint Barthélemy - - - Saint Helena - - - Saint Kitts and Nevis - - - Saint Lucia - - - Saint Martin - - - Saint Paul Island (French Southern and Antarctic Lands) - - - Saint Pierre and Miquelon - - - Saint Pierre and Miquelon Daylight Time - - - Saint Pierre and Miquelon Standard Time - - - Saint Vincent and the Grenadines - - - Sakha (central-east, Russia) - - - Sakha (east, Russia) - - - Sakha (most, Russia) - - - Sakhalin (Russia) - - - Sakhalin Island Time - - - Samara (Russia) - - - Samara Time - - - Samoa - - - Samoa Daylight Time - - - Samoa Standard Time - - - Samoa Time Zone - - - San Marino - - - Saratov (Russia) - - - Saskatchewan (most, Canada) - - - Saudi Arabia - - - Scattered Islands (French Southern and Antarctic Lands) - - - Senegal - - - Serbia - - - Seychelles - - - Seychelles Time - - - Shortcuts - - - Showa Station Time - - - Show military time zone names - PowerToysRun Option - - - Show time names - PowerToysRun Option - - - Show time zone names - PowerToysRun Option - - - Sierra Leone - - - Sierra Time Zone - Military time zone name - - - Sinaloa (Mexico) - - - Singapore - - - Singapore Standard Time - - - Singapore Time - - - Sint Maarten - - - Slovakia - - - Slovenia - - - Solomon Islands - - - Solomon Islands Time - - - Somalia - - - Somaliland - - - Sonora (Mexico) - - - South Africa (most) - - - South African Standard Time - - - South Australia (Australia) - - - South Carolina (United States) - - - South Dakota (most, United States) - - - South East Island (New Zealand) - - - South Georgia and the South Sandwich Islands - - - South Georgia and the South Sandwich Islands Time - - - South Kalimantan (Indonesia) - - - South Korea - - - South Ossetia - - - South Sudan - - - Southampton Island (Canada) - - - Spain (most) - - - Srednekolymsk Time - - - Sri Lanka - - - Sri Lanka Standard Time - - - Standard Time - - - ST - Short for "Standard Time" - - - Star Keys (New Zealand) - - - Sud-Ubangi (Democratic Republic of the Congo) - - - Sudan - - - Sulawesi (Indonesia) - - - Sumatra (Indonesia) - - - SuriName - - - Suriname Time - - - Sverdlovsk (Russia) - - - Sweden - - - Switzerland - - - Syria - - - São Tomé and Príncipe - - - Tahiti Time - - - Taiwan - - - Tajikistan - - - Tajikistan Time - - - Tango Time Zone - Military time zone name - - - Tanzania - - - Tasmania (Australia) - - - Tennessee (most, United States) - - - Texas (most, United States) - - - Thailand - - - Thailand Standard Time - - - The Fort (New Zealand) - - - The Sisters (New Zealand) - - - Thule Air Base (Greenland) - - - Time - - - T - Short for "Time" - - - Time Zone - - - TZ - Short for "Time Zone" - - - Timor Leste Time - - - Togo - - - Tokelau - - - Tokelau Time - - - Tomsk (Russia) - - - Tonga - - - Tonga Time - - - Transnistria - - - Trinidad and Tobago - - - Tshuapa (Democratic Republic of the Congo) - - - Tunisia - - - Turkey - - - Turkey Time - - - Turkmenistan - - - Turkmenistan Time - - - Turks and Caicos Islands - - - Tuva (Russia) - - - Tuvalu - - - Tuvalu Time - - - Tyumen (Russia) - - - U.S. Virgin Islands - - - Udmurtia (Russia) - - - Uganda - - - Ulaanbaatar Standard Time - - - Ulaanbaatar Summer Time - - - Ulyanovsk (Russia) - - - Uniform Time Zone - Military time zone name - - - United Arab Emirates - - - United Arab Emirates Standard Time - - - United Kingdom - - - Uruguay - - - Uruguay Standard Time - - - Uruguay Summer Time - - - Use daylight saving time - - - Utah (United States) - - - UTC - Short for "Coordinated Universal Time" - - - Uvs (Mongolia) - - - Uzbekistan - - - Uzbekistan Time - - - Vanuatu - - - Vanuatu Time - - - Vatican City - - - Venezuela - - - Venezuelan Standard Time - - - Vermont (United States) - - - Victor Time Zone - Military time zone name - - - Victoria (Australia) - - - Vietnam - - - Virginia (United States) - - - Vladivostok Time - - - Volgograd Time - - - Vostok Station Time - - - Wake Island - - - Wake Island Time - - - Wallis and Futuna - - - Washington (United States) - - - West Africa Summer Time - - - West Africa Time - - - West Greenland Summer Time - - - West Greenland Time - - - West Kalimantan (Indonesia) - - - West Kazakhstan (Kazakhstan) - - - West Nusa Tenggara (Indonesia) - - - West Papua (Indonesia) - - - West Virginia (United States) - - - Weste - - - Western Australia Standard Time - - - Western European Summer Time - - - Western European Time - - - Western Indonesian Time - - - Western Standard Time - - - Whiskey Time Zone - Military time zone name - - - Wisconsin (United States) - - - Wyoming (United States) - - - X-ray Time Zone - Military time zone name - - - Yakutsk Time - - - Yamalia (Russia) - - - Yankee Time Zone - Military time zone name - - - Yap (Micronesia) - - - Yekaterinburg Time - - - Yemen - - - Yes - - - Yukon (Canada) - - - Zabaykalsky (Russia) - - - Zambia - - - Zimbabwe - - - Zulu Time Zone - Military time zone name - - - Équateur (Democratic Republic of the Congo) - - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/plugin.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/plugin.json deleted file mode 100644 index a50e19278d..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/plugin.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "ID": "BADD1B06EF0A4B61AD95395F24241D69", - "ActionKeyword": "&", - "IsGlobal": false, - "Name": "Time zone", - "Author": "TobiasSekan", - "Version": "1.0.0", - "Language": "csharp", - "Website": "https://aka.ms/powertoys", - "ExecuteFileName": "Microsoft.PowerToys.Run.Plugin.TimeZone.dll", - "IcoPathDark": "Images\\timeZone.dark.png", - "IcoPathLight": "Images\\timeZone.light.png" -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timeZones.schema.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timeZones.schema.json deleted file mode 100644 index c74e1d96cb..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timeZones.schema.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "TimeZones": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "Offset" ], - "properties": { - "Offset": { - "type": "string", - "description": "The time offset of this time zone (the gap from the UTC time zone, must convertible to a C# TimeSpan)" - }, - "Name": { - "type": "string", - "description": "The name of this time zone." - }, - "MilitaryName": { - "type": "string", - "description": "The military name of this time zone." - }, - "Shortcut": { - "type": "string", - "description": "A shortcut for the names this time zone." - }, - "TimeNamesStandard": { - "type": "array", - "description": "A list with names for the standard time.", - "uniqueItems": true, - "items": { - "type": "string" - } - }, - "TimeNamesDaylight": { - "type": "array", - "description": "A list with names for the daylight saving time.", - "uniqueItems": true, - "items": { - "type": "string" - } - }, - "ShortcutsStandard": { - "type": "array", - "description": "A list with shortcuts for the names for the standard time.", - "uniqueItems": true, - "items": { - "type": "string" - } - }, - "ShortcutsDaylight": { - "type": "array", - "description": "A list with shortcuts for the names for the daylight saving time.", - "uniqueItems": true, - "items": { - "type": "string" - } - }, - "CountriesStandard": { - "type": "array", - "description": "A list with all countries in this time zone that use a standard time.", - "uniqueItems": true, - "items": { - "type": "string" - } - }, - "CountriesDaylight": { - "type": "array", - "description": "A list with all countries in this time zone that use a daylight saving time.", - "uniqueItems": true, - "items": { - "type": "string" - } - } - } - } - } - } -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json deleted file mode 100644 index fcef0af599..0000000000 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeZone/timezones.json +++ /dev/null @@ -1,1375 +0,0 @@ -{ - "$schema": "./timeZones.schema.json", - "TimeZones": - [ - { - "Offset": "-12:00", - "Name": "International Date Line West time zone", - "Shortcut": "IDLW", - "MilitaryName": "Yankee Time Zone", - "TimeNamesStandard": [ - "Baker Island Time" - ], - "ShortcutsStandard": [ - "BIT" - ], - "CountriesStandard": [ - "Baker Island", - "Howland Island" - ] - }, - { - "Offset": "-11:00", - "Name": "Samoa Time Zone", - "MilitaryName": "X-ray Time Zone", - "TimeNamesStandard": [ - "Niue Time", - "Samoa Standard Time" - ], - "ShortcutsStandard": [ - "NUT", - "SST" - ], - "TimeNamesDaylight": [ - "Samoa Daylight Time" - ], - "ShortcutsDaylight": [ - "SDT" - ], - "CountriesStandard": [ - "American Samoa", - "Jarvis Island", - "Kingman Reef", - "Midway Atoll", - "Niue", - "Palmyra Atoll" - ], - "CountriesDaylight": [ - "American Samoa" - ] - }, - { - "Offset": "-10:00", - "Name": "Hawaii-Aleutian Time Zone", - "MilitaryName": "Whiskey Time Zone", - "TimeNamesStandard": [ - "Hawaii-Aleutian Standard Time", - "Tahiti Time" - ], - "ShortcutsStandard": [ - "HST", - "TAHT" - ], - "TimeNamesDaylight": [ - "Hawaii-Aleutian Daylight Time" - ], - "ShortcutsDaylight": [ - "HDT" - ], - "CountriesStandard": [ - "Cook Islands", - "French Polynesia (most)", - "Johnston Atoll", - "Hawaii (United States)" - ], - "CountriesDaylight": [ - "Andreanof Islands (United States)", - "Islands of Four Mountains (United States)", - "Near Islands (United States)", - "Rat Islands (Aleutian Islands, Alaska, United States)" - ] - }, - { - "Offset": "-09:30", - "TimeNamesStandard": [ - "Marquesas Islands Time" - ], - "ShortcutsStandard": [ - "MART", - "MIT" - ], - "CountriesStandard": [ - "Marquesas Islands (French Polynesia)" - ] - }, - { - "Offset": "-09:00", - "Name": "Alaska Time Zone", - "MilitaryName": "Victor Time Zone", - "TimeNamesStandard": [ - "Alaska Standard Time", - "Gambier Islands Time" - ], - "ShortcutsStandard": [ - "AKST", - "GAMT", - "GIT" - ], - "TimeNamesDaylight": [ - "Alaska Daylight Time" - ], - "ShortcutsDaylight": [ - "AKDT" - ], - "CountriesStandard": [ - "Gambier Islands (French Polynesia)" - ], - "CountriesDaylight": [ - "Alaska (most, United States)" - ] - }, - { - "Offset": "-08:00", - "Name": "Pacific Time Zone", - "Shortcut": "PT", - "MilitaryName": "Uniform Time Zone", - "TimeNamesStandard": [ - "Pacific Standard Time" - ], - "ShortcutsStandard": [ - "PST" - ], - "TimeNamesDaylight": [ - "Pacific Daylight Time" - ], - "ShortcutsDaylight": [ - "PDT" - ], - "CountriesStandard": [ - "Clipperton Island", - "Pitcairn Islands" - ], - "CountriesDaylight": [ - "British Columbia (most, Canada)", - "Baja California (Mexico)", - "California (United States)", - "Idaho (north, United States)", - "Nevada (United States)", - "Oregon (most, United States)", - "Washington (United States)" - ] - }, - { - "Offset": "-07:00", - "Name": "Mountain Time Zone", - "MilitaryName": "Tango Time Zone", - "TimeNamesStandard": [ - "Mountain Standard Time" - ], - "ShortcutsStandard": [ - "MST" - ], - "TimeNamesDaylight": [ - "Mountain Daylight Time" - ], - "ShortcutsDaylight": [ - "MDT" - ], - "CountriesStandard": [ - "British Columbia (northeast, Canada)", - "Yukon (Canada)", - "Sonora (Mexico)", - "Arizona (most, United States)" - ], - "CountriesDaylight": [ - "Alberta (Canada)", - "British Columbia (southeast, Canada)", - "Northwest Territories (Canada)", - "Nunavut (west, Canada)", - "Baja California Sur (Mexico)", - "Chihuahua (Mexico)", - "Nayarit (most, Mexico)", - "Sinaloa (Mexico)", - "Colorado (United States)", - "Idaho (most, United States)", - "Montana (United States)", - "New Mexico (United States)", - "Utah (United States)", - "Wyoming (United States)" - ] - }, - { - "Offset": "-06:00", - "Name": "Central Time Zone", - "Shortcut": "CT", - "MilitaryName": "Sierra Time Zone", - "TimeNamesStandard": [ - "Central Standard Time", - "Easter Island Standard Time", - "Galápagos Time" - ], - "ShortcutsStandard": [ - "CST", - "EAST", - "GALT" - ], - "TimeNamesDaylight": [ - "Central Daylight Time", - "Easter Island Summer Time" - ], - "ShortcutsDaylight": [ - "CDT", - "EASST" - ], - "CountriesStandard": [ - "Belize", - "Saskatchewan (most, Canada)", - "Costa Rica", - "Ecuador: Galápagos", - "El Salvador", - "Guatemala", - "Honduras", - "Nicaragua" - ], - "CountriesDaylight": [ - "Manitoba (Canada)", - "Nunavut (central, Canada)", - "Ontario (west, Canada)", - "Easter Island (Chile)", - "Mexico (most)", - "Alabama (United States)", - "Arkansas (United States)", - "Illinois (United States)", - "Iowa (United States)", - "Kansas (most, United States)", - "Louisiana (United States)", - "Minnesota (United States)", - "Mississippi (United States)", - "Missouri (United States)", - "Nebraska (most, United States)", - "North Dakota (most, United States)", - "Oklahoma, United States", - "South Dakota (most, United States)", - "Tennessee (most, United States)", - "Texas (most, United States)", - "Wisconsin (United States)" - ] - }, - { - "Offset": "-05:00", - "Name": "Eastern Time Zone", - "Shortcut": "ET", - "MilitaryName": "Romeo Time Zone", - "TimeNamesStandard": [ - "Acre Time", - "Colombia Time", - "Cuba Standard Time", - "Ecuador Time", - "Eastern Standard Time", - "Peru Time" - ], - "ShortcutsStandard": [ - "ACT", - "COT", - "CST", - "ECT", - "EST", - "PET" - ], - "TimeNamesDaylight": [ - "Cuba Daylight Time", - "Colombia Summer Time", - "Eastern Daylight Time" - ], - "ShortcutsDaylight": [ - "CDT", - "COST", - "EDT" - ], - "CountriesStandard": [ - "Acre (Brazil)", - "Atikokan (Canada)", - "Mishkeegogamang (Canada)", - "Southampton Island (Canada)", - "Cayman Islands", - "Colombia", - "Ecuador (most)", - "Jamaica", - "Quintana Roo (Mexico)", - "Navassa Island", - "Panama", - "Peru" - ], - "CountriesDaylight": [ - "Bahamas", - "Nunavut (east, Canada)", - "Ontario (most, Canada)", - "Quebec (most, Canada)", - "Cuba", - "Haiti", - "Turks and Caicos Islands", - "Connecticut (United States)", - "Delaware (United States)", - "District of Columbia (United States)", - "Florida (most, United States)", - "Georgia (United States)", - "Indiana (most, United States)", - "Kentucky (most, United States)", - "Maine (United States)", - "Maryland (United States)", - "Massachusetts", - "Michigan (most, United States)", - "New Hampshire (United States)", - "New Jersey (United States)", - "New York (United States)", - "North Carolina (United States)", - "Ohio (United States)", - "Pennsylvania (United States)", - "Rhode Island (United States)", - "South Carolina (United States)", - "Vermont (United States)", - "Virginia (United States)", - "West Virginia (United States)" - ] - }, - { - "Offset": "-04:00", - "Name": "Atlantic Time Zone", - "MilitaryName": "Quebec Time Zone", - "TimeNamesStandard": [ - "Amazon Time", - "Atlantic Standard Time", - "Bolivia Time", - "Eastern Caribbean Time", - "Falkland Islands Time", - "Guyana Time", - "Paraguay Time", - "Venezuelan Standard Time" - ], - "ShortcutsStandard": [ - "AMT", - "AST", - "BOT", - "ECT", - "FKT", - "GYT", - "PYT", - "VET" - ], - "TimeNamesDaylight": [ - "Atlantic Daylight Time", - "Amazon Summer Time", - "Chile Summer Time", - "Falkland Islands Summer Time", - "Paraguay Summer Time" - ], - "ShortcutsDaylight": [ - "AMST", - "ADT", - "CLST", - "FKST", - "PYST" - ], - "CountriesStandard": [ - "Anguilla", - "Antigua and Barbuda", - "Aruba", - "Barbados", - "Bolivia", - "Amazonas (most, Brazil)", - "Mato Grosso (Brazil)", - "Mato Grosso do Sul (Brazil)", - "Rondônia (Brazil)", - "Roraima (Brazil)", - "British Virgin Islands", - "Quebec (east, Canada)", - "Caribbean Netherlands", - "Curaçao", - "Dominica", - "Dominican Republic", - "Grenada", - "Guadeloupe", - "Guyana", - "Martinique", - "Montserrat", - "Puerto Rico", - "Saint Barthélemy", - "Saint Kitts and Nevis", - "Saint Lucia", - "Saint Martin", - "Saint Vincent and the Grenadines", - "Sint Maarten", - "Trinidad and Tobago", - "U.S. Virgin Islands", - "Venezuela" - ], - "CountriesDaylight": [ - "Bermuda", - "Labrador (most, Canada)", - "New Brunswick (Canada)", - "Nova Scotia (Canada)", - "Prince Edward Island (Canada)", - "Chile (most)", - "Thule Air Base (Greenland)", - "Paraguay" - ] - }, - { - "Offset": "-03:30", - "Name": "Newfoundland Time Zone", - "Shortcut": "NT", - "TimeNamesStandard": [ - "Newfoundland Standard Time" - ], - "ShortcutsStandard": [ - "NST" - ], - "TimeNamesDaylight": [ - "Newfoundland Daylight Time" - ], - "ShortcutsDaylight": [ - "NDT" - ], - "CountriesStandard": [ - "Labrador (southeast, Canada)" - ], - "CountriesDaylight": [ - "Newfoundland (Canada)" - ] - }, - { - "Offset": "-03:00", - "MilitaryName": "Papa Time Zone", - "TimeNamesStandard": [ - "Argentina Time", - "Brasília Time", - "French Guiana Time", - "Saint Pierre and Miquelon Standard Time", - "Rothera Research Station Time", - "Suriname Time", - "Uruguay Standard Time", - "West Greenland Time" - ], - "ShortcutsStandard": [ - "ART", - "BRT", - "GFT", - "PMST", - "ROTT", - "SRT", - "UYT", - "WGT" - ], - "TimeNamesDaylight": [ - "Brasília Summer Time", - "Saint Pierre and Miquelon Daylight Time", - "Uruguay Summer Time", - "West Greenland Summer Time" - ], - "ShortcutsDaylight": [ - "BRST", - "PMDT", - "WGST", - "UYST" - ], - "CountriesStandard": [ - "Argentina", - "Brazil (most)", - "Magallanes (Chile)", - "Falkland Islands", - "French Guiana", - "SuriName", - "Uruguay" - ], - "CountriesDaylight": [ - "Greenland (most)", - "Saint Pierre and Miquelon" - ] - }, - { - "Offset": "-02:00", - "MilitaryName": "Oscar Time Zone", - "TimeNamesStandard": [ - "Fernando de Noronha Time", - "South Georgia and the South Sandwich Islands Time" - ], - "ShortcutsStandard": [ - "FNT", - "GST" - ], - "CountriesStandard": [ - "Fernando de Noronha (Brazil)", - "South Georgia and the South Sandwich Islands" - ] - }, - { - "Offset": "-01:00", - "MilitaryName": "November Time Zone", - "TimeNamesStandard": [ - "Azores Standard Time", - "Cape Verde Time", - "Eastern Greenland Time" - ], - "ShortcutsStandard": [ - "AZOT", - "CVT", - "EGT" - ], - "TimeNamesDaylight": [ - "Azores Summer Time", - "Eastern Greenland Summer Time" - ], - "ShortcutsDaylight": [ - "AZOST", - "EGST" - ], - "CountriesStandard": [ - "Cape Verde" - ], - "CountriesDaylight": [ - "Ittoqqortoormiit (Greenland)", - "Azores (Portugal)" - ] - }, - { - "Offset": "00:00", - "Name": "Coordinated Universal Time", - "Shortcut": "UTC", - "MilitaryName": "Zulu Time Zone", - "TimeNamesStandard": [ - "Greenwich Mean Time", - "Western European Time" - ], - "ShortcutsStandard": [ - "GMT", - "WET" - ], - "TimeNamesDaylight": [ - "British Summer Time", - "Irish Standard Time", - "Western European Summer Time" - ], - "ShortcutsDaylight": [ - "BST", - "IST", - "WEST" - ], - "CountriesStandard": [ - "Ascension and Tristan da Cunha", - "Burkina Faso", - "Danmarkshavn", - "Gambia", - "Ghana", - "Guinea-Bissau", - "Guinea", - "Iceland", - "Ivory Coast", - "Liberia", - "Mali", - "Mauritania", - "Saint Helena", - "São Tomé and Príncipe", - "Senegal", - "Sierra Leone", - "Togo" - ], - "CountriesDaylight": [ - "Canary Islands", - "Faroe Islands", - "Guernsey", - "Ireland", - "Isle of Man", - "Jersey", - "Portugal (most)", - "United Kingdom" - ] - }, - { - "Offset": "01:00", - "MilitaryName": "Alpha Time Zone", - "TimeNamesStandard": [ - "Central European Time", - "Middle European Time", - "West Africa Time" - ], - "ShortcutsStandard": [ - "CET", - "MET", - "WAT" - ], - "TimeNamesDaylight": [ - "Central European Summer Time", - "Heure Avancée d'Europe Centrale", - "Middle European Summer Time", - "West Africa Summer Time" - ], - "ShortcutsDaylight": [ - "CEST", - "HAEC", - "MEST", - "WAST" - ], - "CountriesStandard": [ - "Algeria", - "Angola", - "Benin", - "Cameroon", - "Central African Republic", - "Chad", - "Congo", - "Équateur (Democratic Republic of the Congo)", - "Kinshasa (Democratic Republic of the Congo)", - "Kongo Central (Democratic Republic of the Congo)", - "Kwango (Democratic Republic of the Congo)", - "Kwilu (Democratic Republic of the Congo)", - "Mai-Ndombe (Democratic Republic of the Congo)", - "Mongala (Democratic Republic of the Congo)", - "Nord-Ubangi (Democratic Republic of the Congo)", - "Sud-Ubangi (Democratic Republic of the Congo)", - "Tshuapa (Democratic Republic of the Congo)", - "Equatorial Guinea", - "Gabon", - "Morocco", - "Niger", - "Nigeria", - "Tunisia", - "Weste" - ], - "CountriesDaylight": [ - "Albania", - "Andorra", - "Austria", - "Belgium", - "Bosnia and Herzegovina", - "Croatia", - "Czech Republic", - "Denmark", - "France (metropolitan)", - "Germany", - "Gibraltar", - "Hungary", - "Italy", - "Kosovo", - "Liechtenstein", - "Luxembourg", - "Malta", - "Monaco", - "Montenegro", - "Netherlands (European)", - "North Macedonia", - "Norway", - "Poland", - "San Marino", - "Serbia", - "Slovakia", - "Slovenia", - "Spain (most)", - "Sweden", - "Switzerland", - "Vatican City" - ] - }, - { - "Offset": "02:00", - "MilitaryName": "Bravo Time Zone", - "TimeNamesStandard": [ - "Central Africa Time", - "Eastern European Time", - "Israel Standard Time", - "Kaliningrad Time", - "South African Standard Time" - ], - "ShortcutsStandard": [ - "CAT", - "EET", - "IST", - "KALT", - "SAST" - ], - "TimeNamesDaylight": [ - "Eastern European Summer Time", - "Israel Daylight Time" - ], - "ShortcutsDaylight": [ - "EEST", - "IDT" - ], - "CountriesStandard": [ - "Botswana", - "Burundi", - "Democratic Republic of the Congo (most)", - "Egypt", - "Eswatini", - "Lesotho", - "Libya", - "Malawi", - "Mozambique", - "Namibia", - "Kaliningrad (Russia)", - "Rwanda", - "South Africa (most)", - "South Sudan", - "Sudan", - "Zambia", - "Zimbabwe" - ], - "CountriesDaylight": [ - "Akrotiri and Dhekelia", - "Bulgaria", - "Cyprus", - "Estonia", - "Finland", - "Greece", - "Israel", - "Jordan", - "Latvia", - "Lebanon", - "Lithuania", - "Moldova", - "Northern Cyprus", - "Palestine", - "Romania", - "Transnistria", - "Syria" - ] - }, - { - "Offset": "03:00", - "MilitaryName": "Charlie Time Zone", - "TimeNamesStandard": [ - "Arabia Standard Time", - "East Africa Time", - "Further-eastern European Time", - "Indian Ocean Time", - "Moscow Time", - "Showa Station Time", - "Turkey Time" - ], - "ShortcutsStandard": [ - "AST", - "EAT", - "FET", - "IOT", - "MSK", - "SYOT", - "TRT" - ], - "CountriesStandard": [ - "Abkhazia", - "Bahrain", - "Belarus", - "Comoros", - "Crimea", - "Djibouti", - "Donetsk PR", - "Eritrea", - "Ethiopia", - "Scattered Islands (French Southern and Antarctic Lands)", - "Iraq", - "Kenya", - "Kuwait", - "Luhansk PR", - "Madagascar", - "Mayotte", - "Qatar", - "Russia (most of European part)", - "Saudi Arabia", - "Somalia", - "Somaliland", - "Prince Edward Islands (South Africa)", - "South Ossetia", - "Tanzania", - "Turkey", - "Uganda", - "Yemen" - ] - }, - { - "Offset": "03:30", - "TimeNamesStandard": [ - "Iran Standard Time" - ], - "ShortcutsStandard": [ - "IRST" - ], - "TimeNamesDaylight": [ - "Iran Daylight Time" - ], - "ShortcutsDaylight": [ - "IRDT" - ], - "CountriesDaylight": [ - "Iran" - ] - }, - { - "Offset": "04:00", - "MilitaryName": "Delta Time Zone", - "TimeNamesDaylight": [ - "Armenia Time", - "Azerbaijan Time", - "Georgia Standard Time", - "Gulf Standard Time", - "Mauritius Time", - "Réunion Time", - "Samara Time", - "Seychelles Time", - "Volgograd Time", - "United Arab Emirates Standard Time" - ], - "ShortcutsStandard": [ - "AMT", - "AZT", - "GET", - "GST", - "MUT", - "RET", - "SAMT", - "SCT", - "VOLT" - ], - "CountriesDaylight": [ - "Armenia", - "Artsakh", - "Azerbaijan", - "Crozet Islands (French Southern and Antarctic Lands)", - "Georgia", - "Mauritius", - "Oman", - "Astrakhan (Russia)", - "Samara (Russia)", - "Saratov (Russia)", - "Udmurtia (Russia)", - "Ulyanovsk (Russia)", - "Réunion", - "Seychelles", - "United Arab Emirates" - ] - }, - { - "Offset": "04:30", - "TimeNamesStandard": [ - "Afghanistan Time" - ], - "ShortcutsStandard": [ - "AFT" - ], - "CountriesStandard": [ - "Afghanistan" - ] - }, - { - "Offset": "05:00", - "MilitaryName": "Echo Time Zone", - "TimeNamesStandard": [ - "Aqtobe Time", - "Heard and McDonald Islands Time", - "Mawson Station Time", - "Maldives Time", - "Oral Time", - "Pakistan Standard Time", - "French Southern and Antarctic Time", - "Tajikistan Time", - "Turkmenistan Time", - "Uzbekistan Time", - "Yekaterinburg Time" - ], - "ShortcutsStandard": [ - "AQTT", - "HMT", - "MAWT", - "MVT", - "ORAT", - "PKT", - "TFT", - "TJT", - "TMT", - "UZT", - "YEKT" - ], - "CountriesStandard": [ - "Amsterdam Island (French Southern and Antarctic Lands)", - "Kerguelen Islands (French Southern and Antarctic Lands)", - "Saint Paul Island (French Southern and Antarctic Lands)", - "Heard Island and McDonald Islands", - "Aktobe (Kazakhstan)", - "Atyrau (Kazakhstan)", - "Baikonur (Kazakhstan)", - "Kyzylorda (Kazakhstan)", - "Mangystau (Kazakhstan)", - "West Kazakhstan (Kazakhstan)", - "Maldives", - "Pakistan", - "Bashkortostan (Russia)", - "Chelyabinsk (Russia)", - "Khanty-Mansi (Russia)", - "Kurgan (Russia)", - "Orenburg (Russia)", - "Perm (Russia)", - "Sverdlovsk (Russia)", - "Tyumen (Russia)", - "Yamalia (Russia)", - "Tajikistan", - "Turkmenistan", - "Uzbekistan" - ] - }, - { - "Offset": "05:30", - "TimeNamesStandard": [ - "Indian Standard Time", - "Sri Lanka Standard Time" - ], - "ShortcutsStandard": [ - "IST", - "SLST" - ], - "CountriesStandard": [ - "India", - "Sri Lanka" - ] - }, - { - "Offset": "05:45", - "TimeNamesStandard": [ - "Nepal Standard Time" - ], - "ShortcutsStandard": [ - "NPT" - ], - "CountriesStandard": [ - "Nepal" - ] - }, - { - "Offset": "06:00", - "MilitaryName": "Foxtrot Time Zone", - "TimeNamesStandard": [ - "Alma-Ata Time", - "British Indian Ocean Time", - "Bangladesh Standard Time", - "Bhutan Time", - "Kyrgyzstan Time", - "Omsk Time", - "Vostok Station Time" - ], - "ShortcutsStandard": [ - "ALMT", - "BIOT", - "BST", - "BTT", - "KGT", - "OMST", - "VOST" - ], - "CountriesStandard": [ - "Bangladesh", - "Bhutan", - "British Indian Ocean Territory", - "Kazakhstan (most)", - "Kyrgyzstan", - "Omsk (Russia)" - ] - }, - { - "Offset": "06:30", - "TimeNamesStandard": [ - "ASEAN Common Time", - "Cocos Islands Time", - "Myanmar Standard Time" - ], - "ShortcutsStandard": [ - "ACT", - "CCT", - "MMT" - ], - "CountriesStandard": [ - "Cocos Islands", - "Myanmar" - ] - }, - { - "Offset": "07:00", - "MilitaryName": "Golf Time Zone", - "TimeNamesStandard": [ - "Christmas Island Time", - "Davis Time", - "Hovd Time", - "Indochina Time", - "Krasnoyarsk Time", - "Novosibirsk Time", - "Thailand Standard Time", - "Western Indonesian Time" - ], - "ShortcutsStandard": [ - "CXT", - "DAVT", - "HOVT", - "ICT", - "KRAT", - "NOVT", - "THA", - "WIB" - ], - "CountriesStandard": [ - "Cambodia", - "Christmas Island", - "Central Kalimantan (Indonesia)", - "Java (Indonesia)", - "Sumatra (Indonesia)", - "West Kalimantan (Indonesia)", - "Laos", - "Bayan-Ölgii (Mongolia)", - "Khovd (Mongolia)", - "Uvs (Mongolia)", - "Altai Krai (Russia)", - "Altai Republic (Russia)", - "Kemerovo (Russia)", - "Khakassia (Russia)", - "Krasnoyarsk (Russia)", - "Novosibirsk (Russia)", - "Tomsk (Russia)", - "Tuva (Russia)", - "Thailand", - "Vietnam" - ] - }, - { - "Offset": "08:00", - "MilitaryName": "Hotel Time Zone", - "TimeNamesStandard": [ - "Australian Western Standard Time", - "Brunei Time", - "Choibalsan Standard Time", - "Clipperton Island Standard Time", - "China Standard Time", - "Hong Kong Time", - "Irkutsk Time", - "Malaysia Standard Time", - "Malaysia Time", - "Philippine Time", - "Philippine Standard Time", - "Singapore Time", - "Singapore Standard Time", - "Ulaanbaatar Standard Time", - "Central Indonesian Time", - "Western Standard Time", - "Western Australia Standard Time" - ], - "ShortcutsStandard": [ - "AWST", - "BNT", - "CHOT", - "CIST", - "CST", - "HKT", - "IRKT", - "MST", - "MYT", - "PHT", - "PHST", - "SST", - "ULAT", - "WITA", - "WST" - ], - "TimeNamesDaylight": [ - "Ulaanbaatar Summer Time" - ], - "ShortcutsDaylight": [ - "ULAST" - ], - "CountriesStandard": [ - "Australia: Western Australia (most)", - "Brunei", - "China", - "Hong Kong", - "Bali (Indonesia)", - "East Kalimantan (Indonesia)", - "East Nusa Tenggara (Indonesia)", - "North Kalimantan (Indonesia)", - "South Kalimantan (Indonesia)", - "Sulawesi (Indonesia)", - "West Nusa Tenggara (Indonesia)", - "Macau", - "Malaysia", - "Mongolia (most)", - "Philippines", - "Buryatia (Russia)", - "Irkutsk (Russia)", - "Singapore", - "Taiwan" - ] - }, - { - "Offset": "08:45", - "TimeNamesStandard": [ - "Australian Central Western Standard Time", - "Central Western Standard Time" - ], - "ShortcutsStandard": [ - "ACWST", - "CWST" - ], - "CountriesStandard": [ - "Caiguna (Australia)", - "Cocklebiddy (Australia)", - "Eucla (Australia)", - "Madura (Australia)", - "Mundrabilla (Australia)", - "Border Village (Australia)" - ] - }, - { - "Offset": "09:00", - "MilitaryName": "India Time Zone", - "TimeNamesStandard": [ - "Choibalsan Summer Time", - "Japan Standard Time", - "Korea Standard Time", - "Palau Time", - "Timor Leste Time", - "Eastern Indonesian Time", - "Yakutsk Time" - ], - "ShortcutsStandard": [ - "CHOST", - "JST", - "KST", - "PWT", - "TLT", - "WIT", - "YAKT" - ], - "CountriesStandard": [ - "East Timor", - "Maluku (Indonesia)", - "North Maluku (Indonesia)", - "Papua (Indonesia)", - "West Papua (Indonesia)", - "Japan", - "North Korea", - "Palau", - "Amur (Russia)", - "Sakha (most, Russia)", - "Zabaykalsky (Russia)", - "South Korea" - ] - }, - { - "Offset": "09:30", - "TimeNamesStandard": [ - "Australian Central Standard Time" - ], - "ShortcutsStandard": [ - "ACST" - ], - "TimeNamesDaylight": [ - "Australian Central Daylight Saving Time" - ], - "ShortcutsDaylight": [ - "ACDT" - ], - "CountriesStandard": [ - "Northern Territory", - "South Australia (Australia)" - ] - }, - { - "Offset": "10:00", - "Name": "Chamorro Time Zone", - "MilitaryName": "Kilo Time Zone", - "TimeNamesStandard": [ - "Australian Eastern Standard Time", - "Australian Eastern Time", - "Chamorro Standard Time", - "Chuuk Time", - "Dumont d'Urville Time", - "Papua New Guinea Time", - "Vladivostok Time" - ], - "ShortcutsStandard": [ - "AEST", - "AET", - "CHST", - "CHUT", - "DDUT", - "PGT", - "VLAT" - ], - "TimeNamesDaylight": [ - "Australian Eastern Daylight Saving Time", - "Lord Howe Summer Time" - ], - "ShortcutsDaylight": [ - "AEDT", - "LHST" - ], - "CountriesStandard": [ - "Queensland (Australia)", - "Guam", - "Chuuk (Micronesia)", - "Yap (Micronesia)", - "Northern Mariana Islands", - "Papua New Guinea (most)", - "Jewish (Russia)", - "Khabarovsk (Russia)", - "Primorsky (Russia)", - "Sakha (central-east, Russia)" - ], - "CountriesDaylight": [ - "Australian Capital Territory (Australia)", - "Jervis Bay Territory (Australia)", - "New South Wales (most, Australia)", - "Tasmania (Australia)", - "Victoria (Australia)" - ] - }, - { - "Offset": "10:30", - "TimeNamesStandard": [ - "Lord Howe Standard Time" - ], - "ShortcutsStandard": [ - "LHST" - ], - "CountriesDaylight": [ - "Lord Howe Island (Australia)" - ] - }, - { - "Offset": "11:00", - "MilitaryName": "Lima Time Zone", - "TimeNamesStandard": [ - "Bougainville Standard Time", - "Kosrae Time", - "Macquarie Island Station Time", - "New Caledonia Time", - "Norfolk Island Time", - "Pohnpei Standard Time", - "Sakhalin Island Time", - "Solomon Islands Time", - "Srednekolymsk Time", - "Vanuatu Time" - ], - "ShortcutsStandard": [ - "BST", - "KOST", - "MIST", - "NCT", - "NFT", - "PONT", - "SAKT", - "SBT", - "SRET", - "VUT" - ], - "CountriesStandard": [ - "Kosrae (Micronesia)", - "Pohnpei (Micronesia)", - "New Caledonia", - "Bougainville (Papua New Guinea)", - "Magadan (Russia)", - "Sakha (east, Russia)", - "Sakhalin (Russia)", - "Solomon Islands", - "Vanuatu" - ], - "CountriesDaylight": [ - "Norfolk Island" - ] - }, - { - "Offset": "12:00", - "MilitaryName": "Mike Time Zone", - "TimeNamesStandard": [ - "Anadyr Time", - "Fiji Time", - "Gilbert Island Time", - "Magadan Time", - "Marshall Islands Time", - "New Zealand Standard Time", - "Kamchatka Time", - "Tuvalu Time", - "Wake Island Time" - ], - "ShortcutsStandard": [ - "ANAT", - "FJT", - "GILT", - "MAGT", - "MHT", - "NZST", - "PETT", - "TVT", - "WAKT" - ], - "CountriesStandard": [ - "Fiji", - "Gilbert Islands (Kiribati)", - "Marshall Islands", - "Nauru", - "Chukotka (Russia)", - "Kamchatka (Russia)", - "Tuvalu", - "Wake Island", - "Wallis and Futuna" - ], - "TimeNamesDaylight": [ - "New Zealand Daylight Time" - ], - "ShortcutsDaylight": [ - "NZDT" - ], - "CountriesDaylight": [ - "New Zealand" - ] - }, - { - "Offset": "12:45", - "TimeNamesStandard": [ - "Chatham Standard Time" - ], - "ShortcutsStandard": [ - "CHAST" - ], - "TimeNamesDaylight": [ - "Chatham Daylight Time" - ], - "ShortcutsDaylight": [ - "CHADT" - ], - "CountriesStandard": [ - "Chatham Islands (New Zealand)", - "Pitt Islands (New Zealand)", - "South East Island (New Zealand)", - "The Fort (New Zealand)", - "Little Mangere Island (New Zealand)", - "Star Keys (New Zealand)", - "The Sisters (New Zealand)", - "Forty-Fours (New Zealand)" - ] - }, - { - "Offset": "13:00", - "TimeNamesStandard": [ - "Phoenix Island Time", - "Tokelau Time", - "Tonga Time" - ], - "ShortcutsStandard": [ - "PHOT", - "TKT", - "TOT" - ], - "CountriesStandard": [ - "Phoenix Islands (Kiribati)", - "Samoa", - "Tokelau", - "Tonga" - ] - }, - { - "Offset": "14:00", - "TimeNamesStandard": [ - "Line Islands Time" - ], - "ShortcutsStandard": [ - "LINT" - ], - "CountriesStandard": [ - "Line Islands (Kiribati)" - ] - } - ] -} diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs index 8065dc46a1..8b4d1591bd 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSetting.cs @@ -9,7 +9,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings /// /// A windows setting /// - internal class WindowsSetting + internal sealed class WindowsSetting { /// /// Initializes a new instance of the class. diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs index 7ce25eb499..e05532cc4b 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Classes/WindowsSettings.cs @@ -10,7 +10,7 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings /// /// A class that contain all possible windows settings /// - internal class WindowsSettings + internal sealed class WindowsSettings { /// /// Initializes a new instance of the class with an empty settings list. diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs index ead193f7ad..7240ee0fdc 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/ResultHelper.cs @@ -45,7 +45,18 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Helper AddOptionalToolTip(entry, result); - resultList.Add(result); + // There is a case with MMC snap-ins where we don't have .msc files fort them. Then we need to show the note for this results in subtitle too. + // These results have mmc.exe as command and their note property is filled. + if (entry.Command == "mmc.exe" && !string.IsNullOrEmpty(entry.Note)) + { + result.SubTitle = result.SubTitle + $"\u0020\u0020\u002D\u0020\u0020{Resources.Note}: {entry.Note}"; // "\u0020\u0020\u002D\u0020\u0020" = "" + } + + // To not show duplicate entries we check the existing results on the list before adding the new entry. Example: Device Manager entry for Control Panel and Device Manager entry for MMC. + if (!resultList.Any(x => x.Title == result.Title)) + { + resultList.Add(result); + } } SetScores(resultList, query); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs index 17871ad847..b4264cb128 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.Designer.cs @@ -294,6 +294,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Microsoft Management Console. + /// + internal static string AppMMC { + get { + return ResourceManager.GetString("AppMMC", resourceCulture); + } + } + /// /// Looks up a localized string similar to Apps & Features. /// @@ -609,6 +618,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Authorization Manager. + /// + internal static string AuthorizationManager { + get { + return ResourceManager.GetString("AuthorizationManager", resourceCulture); + } + } + /// /// Looks up a localized string similar to Automatic file downloads. /// @@ -780,6 +798,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to bthprops.cpl. + /// + internal static string bthprops_cpl { + get { + return ResourceManager.GetString("bthprops.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Calendar. /// @@ -843,6 +870,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Certificates - Current User. + /// + internal static string CertificatesCurrentUser { + get { + return ResourceManager.GetString("CertificatesCurrentUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Certificates - Local Computer. + /// + internal static string CertificatesLocalComputer { + get { + return ResourceManager.GetString("CertificatesLocalComputer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change programs. /// @@ -906,6 +951,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to collab.cpl. + /// + internal static string collab_cpl { + get { + return ResourceManager.GetString("collab.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Color filters. /// @@ -942,6 +996,33 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to COM-Objects. + /// + internal static string ComObjects { + get { + return ResourceManager.GetString("ComObjects", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Component Services. + /// + internal static string ComponentServices { + get { + return ResourceManager.GetString("ComponentServices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Computer Management. + /// + internal static string ComputerManagement { + get { + return ResourceManager.GetString("ComputerManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Connectable devices. /// @@ -1041,6 +1122,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Create and format hard disk partitions. + /// + internal static string CreateAndFormatHardDiskPartitions { + get { + return ResourceManager.GetString("CreateAndFormatHardDiskPartitions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Credential manager. /// @@ -1149,6 +1239,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Windows Defender Firewall with Advanced Security. + /// + internal static string DefenderFirewallAdvancedSecurity { + get { + return ResourceManager.GetString("DefenderFirewallAdvancedSecurity", resourceCulture); + } + } + /// /// Looks up a localized string similar to Delivery Optimization. /// @@ -1203,6 +1302,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Device Manager. + /// + internal static string DeviceManagerSnapIn { + get { + return ResourceManager.GetString("DeviceManagerSnapIn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Devices and printers. /// @@ -1248,6 +1356,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Disk Management. + /// + internal static string DiskManagement { + get { + return ResourceManager.GetString("DiskManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Display. /// @@ -1401,6 +1518,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Event Viewer. + /// + internal static string EventViewer { + get { + return ResourceManager.GetString("EventViewer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Exploit Protection. /// @@ -1500,6 +1626,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Firewall.cpl. + /// + internal static string Firewall_cpl { + get { + return ResourceManager.GetString("Firewall.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Focus assist - Quiet hours. /// @@ -1626,6 +1761,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to GPT. + /// + internal static string GPT { + get { + return ResourceManager.GetString("GPT", resourceCulture); + } + } + /// /// Looks up a localized string similar to Graphics settings. /// @@ -1653,6 +1797,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Group Policy. + /// + internal static string GroupPolicy { + get { + return ResourceManager.GetString("GroupPolicy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to hdwwiz.cpl. + /// + internal static string hdwwiz_cpl { + get { + return ResourceManager.GetString("hdwwiz.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Headset display. /// @@ -1806,6 +1968,33 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to IP Security Monitor. + /// + internal static string IpSecurityMonitor { + get { + return ResourceManager.GetString("IpSecurityMonitor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IP Security Policies on Local Computer. + /// + internal static string IpSecurityPoliciesOnLocalComputer { + get { + return ResourceManager.GetString("IpSecurityPoliciesOnLocalComputer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to irprops.cpl. + /// + internal static string irprops_cpl { + get { + return ResourceManager.GetString("irprops.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Isolated Browsing. /// @@ -1905,6 +2094,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Local Computer Policy. + /// + internal static string LocalGroupPolicy { + get { + return ResourceManager.GetString("LocalGroupPolicy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Local Users and Groups. + /// + internal static string LocalUsersAndGroups { + get { + return ResourceManager.GetString("LocalUsersAndGroups", resourceCulture); + } + } + /// /// Looks up a localized string similar to Location. /// @@ -1968,6 +2175,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to MBR. + /// + internal static string MBR { + get { + return ResourceManager.GetString("MBR", resourceCulture); + } + } + /// /// Looks up a localized string similar to Messaging. /// @@ -2013,6 +2229,195 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to azman.msc. + /// + internal static string MMC_azman { + get { + return ResourceManager.GetString("MMC_azman", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to certlm.msc. + /// + internal static string MMC_certlm { + get { + return ResourceManager.GetString("MMC_certlm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to certmgr.msc. + /// + internal static string MMC_certmgr { + get { + return ResourceManager.GetString("MMC_certmgr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to comexp.msc. + /// + internal static string MMC_comexp { + get { + return ResourceManager.GetString("MMC_comexp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to compmgmt.msc. + /// + internal static string MMC_compmgmt { + get { + return ResourceManager.GetString("MMC_compmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to devmgmt.msc. + /// + internal static string MMC_devmgmt { + get { + return ResourceManager.GetString("MMC_devmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to diskmgmt.msc. + /// + internal static string MMC_diskmgmt { + get { + return ResourceManager.GetString("MMC_diskmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to eventvwr.msc. + /// + internal static string MMC_eventvwr { + get { + return ResourceManager.GetString("MMC_eventvwr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to fsmgmt.msc. + /// + internal static string MMC_fsmgmt { + get { + return ResourceManager.GetString("MMC_fsmgmt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to gpedit.msc. + /// + internal static string MMC_gpedit { + get { + return ResourceManager.GetString("MMC_gpedit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to lusrmgr.msc. + /// + internal static string MMC_lusrmgr { + get { + return ResourceManager.GetString("MMC_lusrmgr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to mmc.exe. + /// + internal static string MMC_mmcexe { + get { + return ResourceManager.GetString("MMC_mmcexe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to perfmon.msc. + /// + internal static string MMC_perfmon { + get { + return ResourceManager.GetString("MMC_perfmon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to printmanagement.msc. + /// + internal static string MMC_printmanagement { + get { + return ResourceManager.GetString("MMC_printmanagement", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to rsop.msc. + /// + internal static string MMC_rsop { + get { + return ResourceManager.GetString("MMC_rsop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to secpol.msc. + /// + internal static string MMC_secpol { + get { + return ResourceManager.GetString("MMC_secpol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to services.msc. + /// + internal static string MMC_services { + get { + return ResourceManager.GetString("MMC_services", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to taskschd.msc. + /// + internal static string MMC_taskschd { + get { + return ResourceManager.GetString("MMC_taskschd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to tpm.msc. + /// + internal static string MMC_tpm { + get { + return ResourceManager.GetString("MMC_tpm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WF.msc. + /// + internal static string MMC_wf { + get { + return ResourceManager.GetString("MMC_wf", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WmiMgmt.msc. + /// + internal static string MMC_wmimgmt { + get { + return ResourceManager.GetString("MMC_wmimgmt", resourceCulture); + } + } + /// /// Looks up a localized string similar to mmsys.cpl. /// @@ -2149,7 +2554,16 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } /// - /// Looks up a localized string similar to Nearby share settings. + /// Looks up a localized string similar to ncpa.cpl. + /// + internal static string ncpa_cpl { + get { + return ResourceManager.GetString("ncpa.cpl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nearby sharing settings. /// internal static string NearbyShareSettings { get { @@ -2211,6 +2625,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Network sessions. + /// + internal static string NetworkSessions { + get { + return ResourceManager.GetString("NetworkSessions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Network Setup Wizard. /// @@ -2409,6 +2832,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to You have to add this snap-in manually.. + /// + internal static string NoteNoMscFileExist { + get { + return ResourceManager.GetString("NoteNoMscFileExist", resourceCulture); + } + } + /// /// Looks up a localized string similar to Added in Windows 10, version 1903 (build 18362).. /// @@ -2706,6 +3138,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Performance Monitor. + /// + internal static string PerformanceMonitor { + get { + return ResourceManager.GetString("PerformanceMonitor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Permissions and history. /// @@ -2850,6 +3291,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to PNP Device. + /// + internal static string PnpDevice { + get { + return ResourceManager.GetString("PnpDevice", resourceCulture); + } + } + /// /// Looks up a localized string similar to Power and sleep. /// @@ -2904,6 +3354,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Printer Spooler. + /// + internal static string PrinterSpooler { + get { + return ResourceManager.GetString("PrinterSpooler", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Print Management. + /// + internal static string PrintManagement { + get { + return ResourceManager.GetString("PrintManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Print screen. /// @@ -3147,6 +3615,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Resultant Set of Policy. + /// + internal static string ResultantSetOfPolicy { + get { + return ResourceManager.GetString("ResultantSetOfPolicy", resourceCulture); + } + } + /// /// Looks up a localized string similar to Scanners and cameras. /// @@ -3255,6 +3732,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Security Configuration and Analysis. + /// + internal static string SecurityConfigurationAndAnalysis { + get { + return ResourceManager.GetString("SecurityConfigurationAndAnalysis", resourceCulture); + } + } + /// /// Looks up a localized string similar to Security Processor. /// @@ -3264,6 +3750,24 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Security Templates. + /// + internal static string SecurityTemplates { + get { + return ResourceManager.GetString("SecurityTemplates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Services. + /// + internal static string ServicesSnapIn { + get { + return ResourceManager.GetString("ServicesSnapIn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Session cleanup. /// @@ -3309,6 +3813,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Shared Folders. + /// + internal static string SharedFolders { + get { + return ResourceManager.GetString("SharedFolders", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shortcuts. /// @@ -3354,6 +3867,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to SMB. + /// + internal static string SMB { + get { + return ResourceManager.GetString("SMB", resourceCulture); + } + } + /// /// Looks up a localized string similar to Sound. /// @@ -3507,6 +4029,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to System Tools. + /// + internal static string SystemTools { + get { + return ResourceManager.GetString("SystemTools", resourceCulture); + } + } + /// /// Looks up a localized string similar to System variables. /// @@ -3534,6 +4065,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to TabletPC.cpl. + /// + internal static string TabletPC_cpl { + get { + return ResourceManager.GetString("TabletPC.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tablet PC settings. /// @@ -3588,6 +4128,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Task Scheduler. + /// + internal static string TaskScheduler { + get { + return ResourceManager.GetString("TaskScheduler", resourceCulture); + } + } + /// /// Looks up a localized string similar to Team Conferencing. /// @@ -3606,6 +4155,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to telephon.cpl. + /// + internal static string telephon_cpl { + get { + return ResourceManager.GetString("telephon.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Text to speech. /// @@ -3687,6 +4245,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to TPM Management. + /// + internal static string TpmManagement { + get { + return ResourceManager.GetString("TpmManagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Transparency. /// @@ -4056,6 +4623,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to Windows Management Instrumentation. + /// + internal static string WindowsManagementInstrumentation { + get { + return ResourceManager.GetString("WindowsManagementInstrumentation", resourceCulture); + } + } + /// /// Looks up a localized string similar to Windows Mobility Center. /// @@ -4155,6 +4731,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to WMI Control. + /// + internal static string WmiControl { + get { + return ResourceManager.GetString("WmiControl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Workplace. /// @@ -4173,6 +4758,15 @@ namespace Microsoft.PowerToys.Run.Plugin.WindowsSettings.Properties { } } + /// + /// Looks up a localized string similar to wscui.cpl. + /// + internal static string wscui_cpl { + get { + return ResourceManager.GetString("wscui.cpl", resourceCulture); + } + } + /// /// Looks up a localized string similar to Wubi IME settings. /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx index 0eab37985e..e3837eb792 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Properties/Resources.resx @@ -217,6 +217,9 @@ App Short/modern name for application + + Microsoft Management Console + Apps & Features Area Apps @@ -333,6 +336,10 @@ Audio and speech Area MixedReality, only available if the Mixed Reality Portal app is installed. + + Authorization Manager + Name of MMC Snap-In. + Automatic file downloads Area Privacy @@ -406,6 +413,9 @@ Broadcasting Area Gaming + + bthprops.cpl + Calendar Area Privacy @@ -433,6 +443,14 @@ Cellular and SIM Area NetworkAndInternet + + Certificates - Current User + Name of MMC Snap-In. + + + Certificates - Local Computer + Name of MMC Snap-In. + Change programs @@ -458,6 +476,9 @@ Closed captions Area EaseOfAccess + + collab.cpl + Color filters Area EaseOfAccess @@ -474,6 +495,17 @@ Command The command to direct start a setting + + COM-Objects + + + Component Services + Name of MMC Snap-In. + + + Computer Management + Name of MMC Snap-In. + Connectable devices @@ -513,6 +545,9 @@ Cortana - Language Area Cortana + + Create and format hard disk partitions + Credential manager Area Control Panel (legacy settings) @@ -557,6 +592,10 @@ Default Save Locations Area System + + Windows Defender Firewall with Advanced Security + Name of MMC Snap-In. + Delivery Optimization Area UpdateAndSecurity @@ -580,6 +619,10 @@ Device manager Area Control Panel (legacy settings) + + Device Manager + Name of MMC Snap-In. + Devices and printers Area Control Panel (legacy settings) @@ -600,6 +643,10 @@ Direct open your phone Area EaseOfAccess + + Disk Management + Name of MMC Snap-In. + Display Area EaseOfAccess @@ -666,6 +713,10 @@ Ethernet Area NetworkAndInternet + + Event Viewer + Name of MMC Snap-In. + Exploit Protection @@ -708,6 +759,9 @@ Firewall + + Firewall.cpl + Focus assist - Quiet hours Area System @@ -764,6 +818,9 @@ Glance Area Personalization, Deprecated in Windows 10, version 1809 and later + + GPT + Graphics settings Area System @@ -775,6 +832,12 @@ Green week Mean you don't can see green colors + + Group Policy + + + hdwwiz.cpl + Headset display Area MixedReality, only available if the Mixed Reality Portal app is installed. @@ -837,6 +900,17 @@ IP Should not translated + + IP Security Monitor + Name of MMC Snap-In. + + + IP Security Policies on Local Computer + Name of MMC Snap-In. + + + irprops.cpl + Isolated Browsing @@ -876,6 +950,14 @@ Light mode + + Local Computer Policy + Name of MMC Snap-In. + + + Local Users and Groups + Name of MMC Snap-In. + Location Area Privacy @@ -904,6 +986,9 @@ Manage optional features Area Apps + + MBR + Messaging Area Privacy @@ -923,6 +1008,69 @@ mlcfg32.cpl File name, Should not translated + + azman.msc + + + certlm.msc + + + certmgr.msc + + + comexp.msc + + + compmgmt.msc + + + devmgmt.msc + + + diskmgmt.msc + + + eventvwr.msc + + + fsmgmt.msc + + + gpedit.msc + + + lusrmgr.msc + + + mmc.exe + + + perfmon.msc + + + printmanagement.msc + + + rsop.msc + + + secpol.msc + + + services.msc + + + taskschd.msc + + + tpm.msc + + + WF.msc + + + WmiMgmt.msc + mmsys.cpl File name, Should not translated @@ -981,6 +1129,9 @@ Navigation bar Area Personalization + + ncpa.cpl + Nearby sharing settings Area System @@ -1009,6 +1160,9 @@ Network properties Area Control Panel (legacy settings) + + Network sessions + Network Setup Wizard Area Control Panel (legacy settings) @@ -1080,6 +1234,9 @@ Only available on mobile and if the enterprise has deployed a provisioning package. + + You have to add this snap-in manually. + Added in Windows 10, version 1903 (build 18362). @@ -1202,6 +1359,10 @@ Performance information and tools Area Control Panel (legacy settings) + + Performance Monitor + Name of MMC Snap-In. + Permissions and history Area Cortana @@ -1263,6 +1424,9 @@ Windows settings + + PNP Device + Power and sleep Area System @@ -1286,6 +1450,13 @@ Printers and scanners Area Device + + Printer Spooler + + + Print Management + Name of MMC Snap-In. + Print screen Mean the "Print screen" key @@ -1388,6 +1559,10 @@ Repair programs + + Resultant Set of Policy + Name of MMC Snap-In. + Scanners and cameras Area Control Panel (legacy settings) @@ -1433,9 +1608,21 @@ Security Center Area Control Panel (legacy settings) + + Security Configuration and Analysis + Name of MMC Snap-In. + Security Processor + + Security Templates + Name of MMC Snap-In. + + + Services + Name of MMC Snap-In. + Session cleanup Area SurfaceHub @@ -1455,6 +1642,10 @@ Shared experience settings Area System + + Shared Folders + Name of MMC Snap-In. + Shortcuts @@ -1474,6 +1665,9 @@ Size Size for text and symbols + + SMB + Sound Area System @@ -1540,6 +1734,9 @@ System properties and Add New Hardware wizard Area Control Panel (legacy settings) + + System Tools + System variables @@ -1551,6 +1748,9 @@ Tablet mode Area System + + TabletPC.cpl + Tablet PC settings Area Control Panel (legacy settings) @@ -1573,6 +1773,10 @@ Tasks Area Privacy + + Task Scheduler + Name of MMC Snap-In. + Team Conferencing Area SurfaceHub @@ -1581,6 +1785,9 @@ Team device management Area SurfaceHub + + telephon.cpl + Text to speech Area Control Panel (legacy settings) @@ -1613,6 +1820,10 @@ Touchpad Area Device + + TPM Management + Name of MMC Snap-In. + Transparency @@ -1767,6 +1978,9 @@ Windows Insider Program Area UpdateAndSecurity + + Windows Management Instrumentation + Windows Mobility Center Area Control Panel (legacy settings) @@ -1810,6 +2024,10 @@ Wireless + + WMI Control + Name of MMC Snap-In. + Workplace @@ -1817,6 +2035,9 @@ Workplace provisioning Area UserAccounts + + wscui.cpl + Wubi IME settings Area TimeAndLanguage, available if the Microsoft Wubi input method editor is installed diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json index 502e41a642..0f5412f463 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/WindowsSettings.json @@ -1284,6 +1284,7 @@ "Name": "ActionCenter", "Areas": [ "AreaSystemAndSecurity" ], "Type": "AppControlPanel", + "AltNames": [ "wscui.cpl" ], "Command": "control /name Microsoft.ActionCenter" }, { @@ -1331,7 +1332,7 @@ }, { "Name": "BluetoothDevices", - "Areas": [ "AreaHardwareAndSound" ], + "Areas": [ "AreaHardwareAndSound", "bthprops.cpl" ], "Type": "AppControlPanel", "Command": "control /name Microsoft.BluetoothDevices" }, @@ -1378,6 +1379,7 @@ "Name": "DeviceManager", "Areas": [ "AreaHardwareAndSound" ], "Type": "AppControlPanel", + "AltNames": [ "hdwwiz.cpl" ], "Command": "control /name Microsoft.DeviceManager" }, { @@ -1437,6 +1439,7 @@ "Name": "Infrared", "Areas": [ "AreaHardwareAndSound" ], "Type": "AppControlPanel", + "AltNames": [ "irprops.cpl" ], "Command": "control /name Microsoft.Infrared" }, { @@ -1469,6 +1472,7 @@ "Name": "NetworkConnection", "Areas": [ "AreaNetworkAndInternet" ], "Type": "AppControlPanel", + "AltNames": [ "ncpa.cpl" ], "Command": "control netconnections" }, { @@ -1531,13 +1535,14 @@ "Name": "PhoneAndModemOptions", "Areas": [ "AreaNetworkAndInternet" ], "Type": "AppControlPanel", + "AltNames": [ "modem.cpl", "telephon.cpl" ], "Command": "control /name Microsoft.PhoneAndModemOptions" }, { "Name": "PhoneAndModem", "Areas": [ "AreaNetworkAndInternet" ], "Type": "AppControlPanel", - "AltNames": [ "modem.cpl" ], + "AltNames": [ "modem.cpl", "telephon.cpl" ], "Command": "control /name Microsoft.PhoneAndModem" }, { @@ -1632,6 +1637,7 @@ "Name": "TabletPcSettings", "Areas": [ "AreaSystemAndSecurity" ], "Type": "AppControlPanel", + "AltNames": [ "TabletPC.cpl" ], "Command": "control /name Microsoft.TabletPCSettings" }, { @@ -1672,7 +1678,7 @@ }, { "Name": "WindowsFirewall", - "Areas": [ "AreaSystemAndSecurity" ], + "Areas": [ "AreaSystemAndSecurity", "Firewall.cpl" ], "Type": "AppControlPanel", "Command": "control /name Microsoft.WindowsFirewall" }, @@ -1794,6 +1800,154 @@ "Type": "AppSettingsApp", "AltNames": [ "ConnectPanel", "ConnectableDevices", "ConnectWirelessAudio", "DeviceDiscovery" ], "Command": "ms-settings-connectabledevices:devicediscovery" + }, + { + "Name": "AppMMC", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "ShowAsFirstResult" : true + }, + { + "Name": "AuthorizationManager", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_azman" ], + "Command": "azman.msc" + }, + { + "Name": "CertificatesCurrentUser", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_certmgr" ], + "Command": "certmgr.msc" + }, + { + "Name": "CertificatesLocalComputer", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_certlm" ], + "Command": "certlm.msc" + }, + { + "Name": "ComponentServices", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_comexp", "ComObjects" ], + "Command": "comexp.msc" + }, + { + "Name": "ComputerManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_compmgmt", "SystemTools", "TaskScheduler", "EventViewer", "SharedFolders", "NetworkSessions", "SMB", "LocalUsersAndGroups", "PerformanceMonitor", "DeviceManager", "PnpDevice", "Storage", "DiskManagement", "CreateAndFormatHardDiskPartitions", "GPT", "MBR", "ServicesSnapIn", "WmiControl", "WindowsManagementInstrumentation" ], + "Command": "compmgmt.msc" + }, + { + "Name": "DeviceManager", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_devmgmt", "PnpDevice" ], + "Command": "devmgmt.msc" + }, + { + "Name": "DiskManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_diskmgmt", "Storage", "CreateAndFormatHardDiskPartitions", "GPT", "MBR" ], + "Command": "diskmgmt.msc" + }, + { + "Name": "EventViewer", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_eventvwr" ], + "Command": "eventvwr.msc" + }, + { + "Name": "LocalGroupPolicy", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_gpedit", "GroupPolicy" ], + "Command": "gpedit.msc" + }, + { + "Name": "IpSecurityMonitor", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "Note": "NoteNoMscFileExist" + }, + { + "Name": "IpSecurityPoliciesOnLocalComputer", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "Note": "NoteNoMscFileExist" + }, + { + "Name": "LocalUsersAndGroups", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_lusrmgr" ], + "Command": "lusrmgr.msc" + }, + { + "Name": "PerformanceMonitor", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_perfmon" ], + "Command": "perfmon.msc" + }, + { + "Name": "PrintManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_printmanagement", "PrinterSpooler" ], + "Command": "printmanagement.msc" + }, + { + "Name": "ResultantSetOfPolicy", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_rsop" ], + "Command": "rsop.msc" + }, + { + "Name": "SecurityConfigurationAndAnalysis", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_secpol" ], + "Command": "secpol.msc" + }, + { + "Name": "SecurityTemplates", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe" ], + "Command": "mmc.exe", + "Note": "NoteNoMscFileExist" + }, + { + "Name": "ServicesSnapIn", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_services" ], + "Command": "services.msc" + }, + { + "Name": "SharedFolders", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_fsmgmt", "NetworkSessions" ], + "Command": "fsmgmt.msc" + }, + { + "Name": "TaskScheduler", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_taskschd" ], + "Command": "taskschd.msc" + }, + { + "Name": "TpmManagement", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_tpm" ], + "Command": "tpm.msc" + }, + { + "Name": "DefenderFirewallAdvancedSecurity", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_wf" ], + "Command": "wf.msc" + }, + { + "Name": "WmiControl", + "Type": "AppMMC", + "AltNames": [ "MMC_mmcexe", "MMC_wmimgmt", "WindowsManagementInstrumentation" ], + "Command": "wmimgmt.msc" } ] } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj index 09b41a6b4a..43df916a4e 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.UnitTests/Microsoft.Plugin.WindowsTerminal.UnitTests.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/src/modules/launcher/PowerLauncher/CustomSearchBox.cs b/src/modules/launcher/PowerLauncher/CustomSearchBox.cs index 5502fd15c9..20b1becf39 100644 --- a/src/modules/launcher/PowerLauncher/CustomSearchBox.cs +++ b/src/modules/launcher/PowerLauncher/CustomSearchBox.cs @@ -9,7 +9,7 @@ using System.Windows.Controls; namespace PowerLauncher { - public class CustomSearchBox : TextBox + public sealed class CustomSearchBox : TextBox { public List ControlledElements { get; } = new List(); @@ -18,7 +18,7 @@ namespace PowerLauncher return new AutoSuggestTextBoxAutomationPeer(this); } - internal class AutoSuggestTextBoxAutomationPeer : TextBoxAutomationPeer + internal sealed class AutoSuggestTextBoxAutomationPeer : TextBoxAutomationPeer { public AutoSuggestTextBoxAutomationPeer(CustomSearchBox owner) : base(owner) diff --git a/src/modules/launcher/PowerLauncher/Helper/DragDataObject.cs b/src/modules/launcher/PowerLauncher/Helper/DragDataObject.cs index b0584bb23d..2e818cda62 100644 --- a/src/modules/launcher/PowerLauncher/Helper/DragDataObject.cs +++ b/src/modules/launcher/PowerLauncher/Helper/DragDataObject.cs @@ -58,7 +58,7 @@ namespace PowerLauncher.Helper { } - // https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/ns-shobjidl_core-shdragimage + // https://learn.microsoft.com/windows/win32/api/shobjidl_core/ns-shobjidl_core-shdragimage [StructLayout(LayoutKind.Sequential)] private struct ShDragImage { @@ -68,7 +68,7 @@ namespace PowerLauncher.Helper public int CrColorKey; } - // https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-idragsourcehelper + // https://learn.microsoft.com/windows/win32/api/shobjidl_core/nn-shobjidl_core-idragsourcehelper [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("DE5BF786-477A-11D2-839D-00C04FD918D0")] private interface IDragSourceHelper diff --git a/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs b/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs index 6cfdd2fded..97a05cfac4 100644 --- a/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs +++ b/src/modules/launcher/PowerLauncher/Helper/KeyboardHelper.cs @@ -7,7 +7,7 @@ using Wox.Plugin; namespace PowerLauncher.Helper { - internal class KeyboardHelper + internal sealed class KeyboardHelper { public static SpecialKeyState CheckModifiers() { diff --git a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj index 4c13b9eb4c..e5c181a31a 100644 --- a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj +++ b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj @@ -84,19 +84,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs b/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs index 1ed201413c..804c162511 100644 --- a/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs +++ b/src/modules/launcher/PowerLauncher/ReportWindow.xaml.cs @@ -17,7 +17,7 @@ using Wox.Plugin.Logger; namespace PowerLauncher { - internal partial class ReportWindow + internal sealed partial class ReportWindow { private static readonly IFileSystem FileSystem = new FileSystem(); private static readonly IFile File = FileSystem.File; diff --git a/src/modules/launcher/PowerLauncher/SettingsReader.cs b/src/modules/launcher/PowerLauncher/SettingsReader.cs index 8764510486..98abae4318 100644 --- a/src/modules/launcher/PowerLauncher/SettingsReader.cs +++ b/src/modules/launcher/PowerLauncher/SettingsReader.cs @@ -166,6 +166,11 @@ namespace PowerLauncher _settings.StartupPosition = overloadSettings.Properties.Position; } + if (_settings.GenerateThumbnailsFromFiles != overloadSettings.Properties.GenerateThumbnailsFromFiles) + { + _settings.GenerateThumbnailsFromFiles = overloadSettings.Properties.GenerateThumbnailsFromFiles; + } + retry = false; } @@ -215,8 +220,7 @@ namespace PowerLauncher private static string GetIcon(PluginMetadata metadata, string iconPath) { - var pluginDirectory = Path.GetFileName(metadata.PluginDirectory); - return Path.Combine(pluginDirectory, iconPath); + return Path.Combine(metadata.PluginDirectory, iconPath); } private static IEnumerable GetDefaultPluginsSettings() @@ -265,9 +269,9 @@ namespace PowerLauncher var defaultOptions = defaultAdditionalOptions.ToDictionary(x => x.Key); foreach (var option in additionalOptions) { - if (option.Key != null && defaultOptions.ContainsKey(option.Key)) + if (option.Key != null && defaultOptions.TryGetValue(option.Key, out PluginAdditionalOption defaultOption)) { - defaultOptions[option.Key].Value = option.Value; + defaultOption.Value = option.Value; } } diff --git a/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs index 6d58d62921..d6949c0321 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/ResultViewModel.cs @@ -5,11 +5,13 @@ using System; using System.Collections.ObjectModel; using System.Globalization; +using System.Threading.Tasks; using System.Windows.Input; using System.Windows.Media; using PowerLauncher.Helper; using PowerLauncher.Plugin; using Wox.Infrastructure.Image; +using Wox.Infrastructure.UserSettings; using Wox.Plugin; using Wox.Plugin.Logger; @@ -23,6 +25,8 @@ namespace PowerLauncher.ViewModel Hover, } + private readonly PowerToysRunSettings _settings; + public ObservableCollection ContextMenuItems { get; } = new ObservableCollection(); public ICommand ActivateContextButtonsHoverCommand { get; } @@ -35,6 +39,9 @@ namespace PowerLauncher.ViewModel private bool _areContextButtonsActive; + private ImageSource _image; + private volatile bool _imageLoaded; + public bool AreContextButtonsActive { get => _areContextButtonsActive; @@ -65,13 +72,15 @@ namespace PowerLauncher.ViewModel public const int NoSelectionIndex = -1; - public ResultViewModel(Result result, IMainViewModel mainViewModel) + public ResultViewModel(Result result, IMainViewModel mainViewModel, PowerToysRunSettings settings) { if (result != null) { Result = result; } + _settings = settings; + ContextMenuSelectedIndex = NoSelectionIndex; LoadContextMenu(); @@ -186,23 +195,49 @@ namespace PowerLauncher.ViewModel { get { - var imagePath = Result.IcoPath; - if (string.IsNullOrEmpty(imagePath) && Result.Icon != null) + if (!_imageLoaded) { - try - { - return Result.Icon(); - } - catch (Exception e) - { - Log.Exception($"IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e, GetType()); - imagePath = ImageLoader.ErrorIconPath; - } + _imageLoaded = true; + _ = LoadImageAsync(); } - // will get here either when icoPath has value\icon delegate is null\when had exception in delegate - return ImageLoader.Load(imagePath); + return _image; } + + private set + { + _image = value; + OnPropertyChanged(nameof(Image)); + } + } + + private async Task LoadImageInternalAsync(string imagePath, Result.IconDelegate icon, bool loadFullImage) + { + if (string.IsNullOrEmpty(imagePath) && icon != null) + { + try + { + var image = icon(); + return image; + } + catch (Exception e) + { + Log.Exception( + $"IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", + e, + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + imagePath = ImageLoader.ErrorIconPath; + } + } + + return await ImageLoader.LoadAsync(imagePath, _settings.GenerateThumbnailsFromFiles, loadFullImage).ConfigureAwait(false); + } + + private async Task LoadImageAsync() + { + var imagePath = Result.IcoPath; + var iconDelegate = Result.Icon; + Image = await LoadImageInternalAsync(imagePath, iconDelegate, false).ConfigureAwait(false); } // Returns false if we've already reached the last item. diff --git a/src/modules/launcher/PowerLauncher/ViewModel/ResultsViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/ResultsViewModel.cs index 825d8a5486..ef7c300976 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/ResultsViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/ResultsViewModel.cs @@ -272,7 +272,7 @@ namespace PowerLauncher.ViewModel List newResults = new List(newRawResults.Count); foreach (Result r in newRawResults) { - newResults.Add(new ResultViewModel(r, _mainViewModel)); + newResults.Add(new ResultViewModel(r, _mainViewModel, _settings)); ct.ThrowIfCancellationRequested(); } diff --git a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs index 02c17e066d..2695f43f0b 100644 --- a/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs +++ b/src/modules/launcher/Wox.Infrastructure/Exception/ExceptionFormatter.cs @@ -91,23 +91,23 @@ namespace Wox.Infrastructure.Exception // GlobalAssemblyCache - .NET Core and .NET 5 and later: false in all cases. // Source https://learn.microsoft.com/dotnet/api/system.reflection.assembly.globalassemblycache?view=net-6.0 - foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { sb.Append("* "); - sb.Append(ass.FullName); + sb.Append(assembly.FullName); sb.Append(" ("); - if (ass.IsDynamic) + if (assembly.IsDynamic) { sb.Append("dynamic assembly doesn't has location"); } - else if (string.IsNullOrEmpty(ass.Location)) + else if (string.IsNullOrEmpty(assembly.Location)) { sb.Append("location is null or empty"); } else { - sb.Append(ass.Location); + sb.Append(assembly.Location); } sb.AppendLine(")"); @@ -116,7 +116,7 @@ namespace Wox.Infrastructure.Exception return sb.ToString(); } - // http://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx + // http://msdn.microsoft.com/library/hh925568%28v=vs.110%29.aspx private static List GetFrameworkVersionFromRegistry() { try diff --git a/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs b/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs index 1aa1b4ea36..8a35469178 100644 --- a/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs +++ b/src/modules/launcher/Wox.Infrastructure/Hotkey/HotkeyModel.cs @@ -154,9 +154,7 @@ namespace Wox.Infrastructure.Hotkey if (CharKey != Key.None) { - text += _specialSymbolDictionary.ContainsKey(CharKey) - ? _specialSymbolDictionary[CharKey] - : CharKey.ToString(); + text += _specialSymbolDictionary.TryGetValue(CharKey, out string value) ? value : CharKey.ToString(); } else if (!string.IsNullOrEmpty(text)) { diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs index 69acf84dd2..0846391a01 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageHashGenerator.cs @@ -34,12 +34,7 @@ namespace Wox.Infrastructure.Image enc.Frames.Add(bitmapFrame); enc.Save(outStream); var byteArray = outStream.GetBuffer(); - - using (var sha1 = SHA1.Create()) - { - var hash = Convert.ToBase64String(sha1.ComputeHash(byteArray)); - return hash; - } + return Convert.ToBase64String(SHA1.HashData(byteArray)); } } catch (System.Exception e) diff --git a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs index 3cc5832c5c..fab64e67f1 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; using ManagedCommon; +using Wox.Infrastructure.UserSettings; using Wox.Plugin; using Wox.Plugin.Logger; @@ -63,12 +64,12 @@ namespace Wox.Infrastructure.Image UpdateIconPath(theme); Task.Run(() => { - Stopwatch.Normal("ImageLoader.Initialize - Preload images cost", () => + Stopwatch.Normal("ImageLoader.Initialize - Preload images cost", async () => { - ImageCache.Usage.AsParallel().ForAll(x => + foreach (var (path, _) in ImageCache.Usage) { - Load(x.Key); - }); + await LoadAsync(path, true); + } }); Log.Info($"Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}", MethodBase.GetCurrentMethod().DeclaringType); @@ -119,10 +120,9 @@ namespace Wox.Infrastructure.Image Cache, } - private static ImageResult LoadInternal(string path, bool loadFullImage = false) + private static async ValueTask LoadInternalAsync(string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) { - ImageSource image; - ImageType type = ImageType.Error; + ImageResult imageResult; try { if (string.IsNullOrEmpty(path)) @@ -143,66 +143,92 @@ namespace Wox.Infrastructure.Image return new ImageResult(imageSource, ImageType.Data); } - if (!Path.IsPathRooted(path)) - { - path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path)); - } - - if (Directory.Exists(path)) - { - /* Directories can also have thumbnails instead of shell icons. - * Generating thumbnails for a bunch of folders while scrolling through - * results from Everything makes a big impact on performance and - * Wox responsibility. - * - Solution: just load the icon - */ - type = ImageType.Folder; - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); - } - else if (File.Exists(path)) - { - // Using InvariantCulture since this is internal - var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture); - if (ImageExtensions.Contains(extension)) - { - type = ImageType.ImageFile; - if (loadFullImage) - { - image = LoadFullImage(path); - } - else - { - /* Although the documentation for GetImage on MSDN indicates that - * if a thumbnail is available it will return one, this has proved to not - * be the case in many situations while testing. - * - Solution: explicitly pass the ThumbnailOnly flag - */ - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly); - } - } - else - { - type = ImageType.File; - image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.None); - } - } - else - { - image = ImageCache[ErrorIconPath]; - path = ErrorIconPath; - } - - if (type != ImageType.Error) - { - image.Freeze(); - } + imageResult = await Task.Run(() => GetThumbnailResult(ref path, generateThumbnailsFromFiles, loadFullImage)); } catch (System.Exception e) { Log.Exception($"Failed to get thumbnail for {path}", e, MethodBase.GetCurrentMethod().DeclaringType); - type = ImageType.Error; - image = ImageCache[ErrorIconPath]; + ImageSource image = ImageCache[ErrorIconPath]; ImageCache[path] = image; + imageResult = new ImageResult(image, ImageType.Error); + } + + return imageResult; + } + + private static ImageResult GetThumbnailResult(ref string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) + { + ImageSource image; + ImageType type = ImageType.Error; + + if (!Path.IsPathRooted(path)) + { + path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path)); + } + + if (Directory.Exists(path)) + { + /* Directories can also have thumbnails instead of shell icons. + * Generating thumbnails for a bunch of folders while scrolling through + * results from Everything makes a big impact on performance and + * Wox responsibility. + * - Solution: just load the icon + */ + type = ImageType.Folder; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } + else if (File.Exists(path)) + { + // Using InvariantCulture since this is internal + var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture); + if (ImageExtensions.Contains(extension)) + { + type = ImageType.ImageFile; + if (loadFullImage) + { + image = LoadFullImage(path); + } + else + { + // PowerToys Run internal images are png, so we make this exception + if (extension == ".png" || generateThumbnailsFromFiles) + { + /* Although the documentation for GetImage on MSDN indicates that + * if a thumbnail is available it will return one, this has proved to not + * be the case in many situations while testing. + * - Solution: explicitly pass the ThumbnailOnly flag + */ + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly); + } + else + { + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } + } + } + else if (!generateThumbnailsFromFiles || (extension == ".pdf" && WindowsThumbnailProvider.DoesPdfUseAcrobatAsProvider())) + { + // The PDF thumbnail provider from Adobe Reader and Acrobat Pro lets crash PT Run with an Dispatcher exception. (https://github.com/microsoft/PowerToys/issues/18166) + // To not run into the crash, we only request the icon of PDF files if the PDF thumbnail handler is set to Adobe Reader/Acrobat Pro. + // Also don't get thumbnail if the GenerateThumbnailsFromFiles option is off. + type = ImageType.File; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.IconOnly); + } + else + { + type = ImageType.File; + image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, Constant.ThumbnailSize, ThumbnailOptions.RESIZETOFIT); + } + } + else + { + image = ImageCache[ErrorIconPath]; + path = ErrorIconPath; + } + + if (type != ImageType.Error) + { + image.Freeze(); } return new ImageResult(image, type); @@ -210,9 +236,9 @@ namespace Wox.Infrastructure.Image private const bool _enableImageHash = true; - public static ImageSource Load(string path, bool loadFullImage = false) + public static async ValueTask LoadAsync(string path, bool generateThumbnailsFromFiles, bool loadFullImage = false) { - var imageResult = LoadInternal(path, loadFullImage); + var imageResult = await LoadInternalAsync(path, generateThumbnailsFromFiles, loadFullImage); var img = imageResult.ImageSource; if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache) diff --git a/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs b/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs index 267b062c09..8200a6409e 100644 --- a/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs +++ b/src/modules/launcher/Wox.Infrastructure/Image/WindowsThumbnailProvider.cs @@ -4,17 +4,20 @@ using System; using System.IO.Abstractions; +using System.Reflection; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; +using Microsoft.Win32; +using Wox.Plugin.Logger; namespace Wox.Infrastructure.Image { [Flags] public enum ThumbnailOptions { - None = 0x00, + RESIZETOFIT = 0x00, BiggerSizeOk = 0x01, InMemoryOnly = 0x02, IconOnly = 0x04, @@ -125,5 +128,97 @@ namespace Wox.Infrastructure.Image throw new InvalidComObjectException($"Error while extracting thumbnail for {fileName}", Marshal.GetExceptionForHR((int)hr)); } + + private static bool logReportedAdobeReaderDetected; // Keep track if Adobe Reader detection has been logged yet. + private static bool logReportedErrorInDetectingAdobeReader; // Keep track if we reported an exception while trying to detect Adobe Reader yet. + private static bool adobeReaderDetectionLastResult; // The last result when Adobe Reader detection has read the registry. + private static DateTime adobeReaderDetectionLastTime; // The last time when Adobe Reader detection has read the registry. + + // We have to evaluate this in real time to not crash, if the user switches to Adobe Reader/Acrobat Pro after starting PT Run. + // Adobe registers its thumbnail handler always in "HKCR\Acrobat.Document.*\shellex\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}". + public static bool DoesPdfUseAcrobatAsProvider() + { + // If the last run is not more than five seconds ago use its result. + // Doing this we minimize the amount of Registry queries, if more than one new PDF file is shown in the results. + if ((DateTime.Now - adobeReaderDetectionLastTime).TotalSeconds < 5) + { + return adobeReaderDetectionLastResult; + } + + // If cache condition is false, then query the registry. + try + { + // First detect if there is a provider other than Adobe. For example PowerToys. + // Generic GUIDs used by Explorer to identify the configured provider types: {E357FCCD-A995-4576-B01F-234630154E96} = File thumbnail, {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} = Image thumbnail. + RegistryKey key1 = Registry.ClassesRoot.OpenSubKey(".pdf\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}", false); + string value1 = (string)key1?.GetValue(string.Empty); + key1?.Close(); + RegistryKey key2 = Registry.ClassesRoot.OpenSubKey(".pdf\\shellex\\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}", false); + string value2 = (string)key2?.GetValue(string.Empty); + key2?.Close(); + if (!string.IsNullOrEmpty(value1) || !string.IsNullOrEmpty(value2)) + { + // A provider other than Adobe is used. (For example the PowerToys PDF Thumbnail provider.) + logReportedAdobeReaderDetected = false; // Reset log marker to report when Adobe is reused. (Example: Adobe -> Test PowerToys. -> Back to Adobe.) + adobeReaderDetectionLastResult = false; + adobeReaderDetectionLastTime = DateTime.Now; + return false; + } + + // Then detect if Adobe is the default PDF application. + // The global config can be found under "HKCR\.pdf", but the "UserChoice" key under HKCU has precedence. + RegistryKey pdfKeyUser = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.pdf\\UserChoice", false); + string pdfAppUser = (string)pdfKeyUser?.GetValue("ProgId"); + pdfKeyUser?.Close(); + RegistryKey pdfKeyGlobal = Registry.ClassesRoot.OpenSubKey(".pdf", false); + string pdfAppGlobal = (string)pdfKeyGlobal?.GetValue(string.Empty); + pdfKeyGlobal?.Close(); + string pdfApp = !string.IsNullOrEmpty(pdfAppUser) ? pdfAppUser : pdfAppGlobal; // User choice has precedence. + if (string.IsNullOrEmpty(pdfApp) || !pdfApp.StartsWith("Acrobat.Document.", StringComparison.OrdinalIgnoreCase)) + { + // Adobe is not used as PDF application. + logReportedAdobeReaderDetected = false; // Reset log marker to report when Adobe is reused. (Example: Adobe -> Test PowerToys. -> Back to Adobe.) + adobeReaderDetectionLastResult = false; + adobeReaderDetectionLastTime = DateTime.Now; + return false; + } + + // Detect if the thumbnail handler from Adobe is disabled. + RegistryKey adobeAppKey = Registry.ClassesRoot.OpenSubKey(pdfApp + "\\shellex\\{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}", false); + string adobeAppProvider = (string)adobeAppKey?.GetValue(string.Empty); + adobeAppKey?.Close(); + if (string.IsNullOrEmpty(adobeAppProvider)) + { + // No Adobe handler. + logReportedAdobeReaderDetected = false; // Reset log marker to report when Adobe is reused. (Example: Adobe -> Test PowerToys. -> Back to Adobe.) + adobeReaderDetectionLastResult = false; + adobeReaderDetectionLastTime = DateTime.Now; + return false; + } + + // Thumbnail handler from Adobe is enabled and used. + if (!logReportedAdobeReaderDetected) + { + logReportedAdobeReaderDetected = true; + Log.Info("Adobe Reader / Adobe Acrobat Pro has been detected as the PDF thumbnail provider.", MethodBase.GetCurrentMethod().DeclaringType); + } + + adobeReaderDetectionLastResult = true; + adobeReaderDetectionLastTime = DateTime.Now; + return true; + } + catch (System.Exception ex) + { + if (!logReportedErrorInDetectingAdobeReader) + { + logReportedErrorInDetectingAdobeReader = true; + Log.Exception("Got an exception while trying to detect Adobe Reader / Adobe Acrobat Pro as PDF thumbnail provider. To prevent PT Run from a Dispatcher crash, we report that Adobe Reader / Adobe Acrobat Pro is used and show only the PDF icon in the results.", ex, MethodBase.GetCurrentMethod().DeclaringType); + } + + // If we fail to detect it, we return that Adobe is used. Otherwise we could run into the Dispatcher crash. + // (This only results in showing the icon instead of a thumbnail. It has no other functional impact.) + return true; + } + } } } diff --git a/src/modules/launcher/Wox.Infrastructure/UserSettings/PowerToysRunSettings.cs b/src/modules/launcher/Wox.Infrastructure/UserSettings/PowerToysRunSettings.cs index 46d44822c8..173de229bb 100644 --- a/src/modules/launcher/Wox.Infrastructure/UserSettings/PowerToysRunSettings.cs +++ b/src/modules/launcher/Wox.Infrastructure/UserSettings/PowerToysRunSettings.cs @@ -323,6 +323,8 @@ namespace Wox.Infrastructure.UserSettings public bool StartedFromPowerToysRunner { get; set; } + public bool GenerateThumbnailsFromFiles { get; set; } = true; + public HttpProxy Proxy { get; set; } = new HttpProxy(); [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj index 317f2215bb..efe9beb24e 100644 --- a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -41,9 +41,9 @@ - - - - + + + + \ No newline at end of file diff --git a/src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs b/src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs new file mode 100644 index 0000000000..a110097f16 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/Common/Interfaces/IShellItem.cs @@ -0,0 +1,46 @@ +// 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. + +using System; +using System.Runtime.InteropServices; +using Wox.Plugin.Common.Win32; + +namespace Wox.Plugin.Common.Interfaces +{ + /// + /// The following are ShellItem DisplayName types. + /// + [Flags] + public enum SIGDN : uint + { + NORMALDISPLAY = 0, + PARENTRELATIVEPARSING = 0x80018001, + PARENTRELATIVEFORADDRESSBAR = 0x8001c001, + DESKTOPABSOLUTEPARSING = 0x80028000, + PARENTRELATIVEEDITING = 0x80031001, + DESKTOPABSOLUTEEDITING = 0x8004c000, + FILESYSPATH = 0x80058000, + URL = 0x80068000, + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")] + public interface IShellItem + { + void BindToHandler( + IntPtr pbc, + [MarshalAs(UnmanagedType.LPStruct)] Guid bhid, + [MarshalAs(UnmanagedType.LPStruct)] Guid riid, + out IntPtr ppv); + + void GetParent(out IShellItem ppsi); + + void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); + + void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs); + + void Compare(IShellItem psi, uint hint, out int piOrder); + } +} diff --git a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs index 4ce9e0cb83..2ea86afd02 100644 --- a/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs +++ b/src/modules/launcher/Wox.Plugin/Common/ShellLocalization.cs @@ -2,67 +2,51 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; -using System.Text; +using Wox.Plugin.Common.Interfaces; +using Wox.Plugin.Common.Win32; namespace Wox.Plugin.Common { /// /// Class to get localized name of shell items like 'My computer'. The localization is based on the 'windows display language'. - /// Reused code from https://stackoverflow.com/questions/41423491/how-to-get-localized-name-of-known-folder for the method /// - public static class ShellLocalization + public class ShellLocalization { - internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001; - internal const uint LOADLIBRARYASDATAFILE = 0x00000002; - - [DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)] - internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes); - - [DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)] - internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")] - internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); - - [DllImport("kernel32.dll", ExactSpelling = true)] - internal static extern int FreeLibrary(IntPtr hModule); - - [DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)] - internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize); + // Cache for already localized names. This makes localization of already localized string faster. + private Dictionary _localizationCache = new Dictionary(); /// /// Returns the localized name of a shell item. /// /// Path to the shell item (e. g. shortcut 'File Explorer.lnk'). /// The localized name as string or . - public static string GetLocalizedName(string path) + public string GetLocalizedName(string path) { - StringBuilder resourcePath = new StringBuilder(1024); - StringBuilder localizedName = new StringBuilder(1024); - int len, id; - len = resourcePath.Capacity; - - // If there is no resource to localize a file name the method returns a non zero value. - if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0) + // Checking cache if path is already localized + if (_localizationCache.TryGetValue(path.ToLowerInvariant(), out string value)) { - _ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity); - IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE); - if (hMod != IntPtr.Zero) - { - if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0) - { - string lString = localizedName.ToString(); - _ = FreeLibrary(hMod); - return lString; - } - - _ = FreeLibrary(hMod); - } + return value; } - return string.Empty; + Guid shellItemType = ShellItemTypeConstants.ShellItemGuid; + int retCode = NativeMethods.SHCreateItemFromParsingName(path, IntPtr.Zero, ref shellItemType, out IShellItem shellItem); + if (retCode != 0) + { + return string.Empty; + } + + shellItem.GetDisplayName(SIGDN.NORMALDISPLAY, out string filename); + + if (!_localizationCache.ContainsKey(path.ToLowerInvariant())) + { + // The if condition is required to not get timing problems when called from an parallel execution. + // Without the check we will get "key exists" exceptions. + _localizationCache.Add(path.ToLowerInvariant(), filename); + } + + return filename; } /// @@ -70,7 +54,7 @@ namespace Wox.Plugin.Common /// /// The path to localize /// The localized path or the original path if localized version is not available - public static string GetLocalizedPath(string path) + public string GetLocalizedPath(string path) { path = Environment.ExpandEnvironmentVariables(path); string ext = Path.GetExtension(path); @@ -79,6 +63,14 @@ namespace Wox.Plugin.Common for (int i = 0; i < pathParts.Length; i++) { + if (i == 0 && pathParts[i].EndsWith(':')) + { + // Skip the drive letter. + locPath[0] = pathParts[0]; + continue; + } + + // Localize path. int iElements = i + 1; string lName = GetLocalizedName(string.Join("\\", pathParts[..iElements])); locPath[i] = !string.IsNullOrEmpty(lName) ? lName : pathParts[i]; diff --git a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs index d7f1228f7e..432d7ea5f5 100644 --- a/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs +++ b/src/modules/launcher/Wox.Plugin/Common/Win32/NativeMethods.cs @@ -5,6 +5,7 @@ using System; using System.Runtime.InteropServices; using System.Text; +using Wox.Plugin.Common.Interfaces; using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute; #pragma warning disable SA1649, CA1051, CA1707, CA1028, CA1714, CA1069, SA1402 @@ -114,6 +115,9 @@ namespace Wox.Plugin.Common.Win32 [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream); + + [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem); } [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These are the names used by win32.")] @@ -141,6 +145,19 @@ namespace Wox.Plugin.Common.Win32 public const int SC_CLOSE = 0xF060; } + public static class ShellItemTypeConstants + { + /// + /// Guid for type IShellItem. + /// + public static readonly Guid ShellItemGuid = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"); + + /// + /// Guid for type IShellItem2. + /// + public static readonly Guid ShellItem2Guid = new Guid("7E9FB0D3-919F-4307-AB2E-9B1860310C93"); + } + public enum HRESULT : uint { /// @@ -206,7 +223,7 @@ namespace Wox.Plugin.Common.Win32 /// /// The operation was canceled by the user. (Error source 7 means Win32.) /// - /// + /// /// E_CANCELLED = 0x800704C7, } diff --git a/src/modules/launcher/Wox.Plugin/PluginPair.cs b/src/modules/launcher/Wox.Plugin/PluginPair.cs index 34df02d3f1..52a08041a1 100644 --- a/src/modules/launcher/Wox.Plugin/PluginPair.cs +++ b/src/modules/launcher/Wox.Plugin/PluginPair.cs @@ -67,10 +67,11 @@ namespace Wox.Plugin { Metadata.Disabled = false; InitializePlugin(api); + if (!IsPluginInitialized) { - var title = string.Format(CultureInfo.CurrentCulture, Resources.FailedToLoadPluginTitle, Metadata.Name); - api.ShowMsg(title, Resources.FailedToLoadPluginDescription, string.Empty, false); + string description = $"{Resources.FailedToLoadPluginDescription} {Metadata.Name}\n\n{Resources.FailedToLoadPluginDescriptionPartTwo}"; + api.ShowMsg(Resources.FailedToLoadPluginTitle, description, string.Empty, false); } } else diff --git a/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs b/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs index ee8e8c9c45..b90d429f40 100644 --- a/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs @@ -61,7 +61,7 @@ namespace Wox.Plugin.Properties { } /// - /// Looks up a localized string similar to Please contact plugin creator for help. + /// Looks up a localized string similar to Fail to load plugin:. /// public static string FailedToLoadPluginDescription { get { @@ -70,7 +70,16 @@ namespace Wox.Plugin.Properties { } /// - /// Looks up a localized string similar to Fail to Load {0} Plugin. + /// Looks up a localized string similar to Please report the bug to https://aka.ms/powerToysReportBug. (For third-party plugins, please contact the plugin author.). + /// + public static string FailedToLoadPluginDescriptionPartTwo { + get { + return ResourceManager.GetString("FailedToLoadPluginDescriptionPartTwo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to PowerToys Run - Plugin Loading Error. /// public static string FailedToLoadPluginTitle { get { diff --git a/src/modules/launcher/Wox.Plugin/Properties/Resources.resx b/src/modules/launcher/Wox.Plugin/Properties/Resources.resx index 595542f0ed..c8b8b2e9a7 100644 --- a/src/modules/launcher/Wox.Plugin/Properties/Resources.resx +++ b/src/modules/launcher/Wox.Plugin/Properties/Resources.resx @@ -118,11 +118,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Please contact plugin creator for help + Fail to load plugin: + + + Please report the bug to https://aka.ms/powerToysReportBug. (For third-party plugins, please contact the plugin author.) + "https://aka.ms/powerToysReportBug" is a web uri. - Fail to Load {0} Plugin - Do not translate '{0}' as this is a place holder and will be converted in code. + PowerToys Run - Plugin Loading Error + Don't translate "PowerToys Run". This is a product name. On all Desktops diff --git a/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs b/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs index a987eefad1..44ed05640a 100644 --- a/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs +++ b/src/modules/launcher/Wox.Plugin/UserSelectedRecord.cs @@ -39,11 +39,7 @@ namespace Wox.Plugin throw new ArgumentNullException(nameof(result)); } - var key = result.ToString(); - if (Records.ContainsKey(result.ToString())) - { - Records.Remove(result.ToString()); - } + Records.Remove(result.ToString()); } public void Add(Result result) diff --git a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj index f9ef5f68fa..c08e738e99 100644 --- a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj +++ b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/modules/launcher/Wox.Test/ResultViewModelTest.cs b/src/modules/launcher/Wox.Test/ResultViewModelTest.cs index 5ce400fcc6..42b58c7151 100644 --- a/src/modules/launcher/Wox.Test/ResultViewModelTest.cs +++ b/src/modules/launcher/Wox.Test/ResultViewModelTest.cs @@ -26,7 +26,7 @@ namespace Wox.Test var result = new Result(); contextMenuResult = new ContextMenuResult(); mainViewModelMock = new Mock(); - resultViewModel = new ResultViewModel(result, mainViewModelMock.Object); + resultViewModel = new ResultViewModel(result, mainViewModelMock.Object, null); var pluginMock = new Mock(); pluginMock.As().Setup(x => x.LoadContextMenus(result)).Returns(new List { contextMenuResult }); diff --git a/src/modules/launcher/Wox.Test/ResultsViewModelTest.cs b/src/modules/launcher/Wox.Test/ResultsViewModelTest.cs index c458b56b18..5ac53ba3b1 100644 --- a/src/modules/launcher/Wox.Test/ResultsViewModelTest.cs +++ b/src/modules/launcher/Wox.Test/ResultsViewModelTest.cs @@ -18,7 +18,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); rvm.SelectedItem = selectedItem; @@ -32,7 +32,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); rvm.SelectedItem = selectedItem; @@ -49,7 +49,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); rvm.SelectedItem = selectedItem; @@ -66,7 +66,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); @@ -88,7 +88,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); rvm.SelectedItem = selectedItem; @@ -106,7 +106,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); rvm.SelectedItem = selectedItem; @@ -124,7 +124,7 @@ namespace Wox.Test // Arrange ResultsViewModel rvm = new ResultsViewModel(); Result result = new Result(); - ResultViewModel selectedItem = new ResultViewModel(result, null); + ResultViewModel selectedItem = new ResultViewModel(result, null, null); selectedItem.ContextMenuItems.Add(new ContextMenuItemViewModel(null, null, null, null, Key.None, ModifierKeys.None, null)); rvm.SelectedItem = selectedItem; diff --git a/src/modules/launcher/Wox.Test/Wox.Test.csproj b/src/modules/launcher/Wox.Test/Wox.Test.csproj index 28220915d4..013bb487f2 100644 --- a/src/modules/launcher/Wox.Test/Wox.Test.csproj +++ b/src/modules/launcher/Wox.Test/Wox.Test.csproj @@ -47,11 +47,11 @@ - - - - - + + + + + diff --git a/src/modules/launcher/Wox.Test/WoxTest.cs b/src/modules/launcher/Wox.Test/WoxTest.cs index d682d300cc..6d35aeda66 100644 --- a/src/modules/launcher/Wox.Test/WoxTest.cs +++ b/src/modules/launcher/Wox.Test/WoxTest.cs @@ -14,7 +14,7 @@ namespace Wox.Test public class WoxTest { // A Dummy class to test that OnPropertyChanged() is called while we set the variable - private class DummyTestClass : BaseModel + private sealed class DummyTestClass : BaseModel { public bool IsFunctionCalled { get; set; } diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc b/src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc new file mode 100644 index 0000000000..b30e3923c9 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlain.base.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h new file mode 100644 index 0000000000..d72f7b3242 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainConstants.h @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace PastePlainConstants +{ + // Name of the powertoy module. + inline const std::wstring ModuleKey = L"PastePlain"; +} \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj new file mode 100644 index 0000000000..8e1b3707b7 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj @@ -0,0 +1,85 @@ + + + + + + + + 15.0 + Win32Proj + {FC373B24-3293-453C-AAF5-CF2909DCEE6A} + PastePlain + PastePlainModuleInterface + PowerToys.PastePlainModuleInterface + + + + DynamicLibrary + v143 + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\PastePlain\ + + + + EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + + + Create + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + Designer + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters new file mode 100644 index 0000000000..9f2d585a7d --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/PastePlainModuleInterface.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {875a08c6-f610-4667-bd0f-80171ed96072} + + + + + Header Files + + + Generated Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Resource Files + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/Resources.resx b/src/modules/pasteplain/PastePlainModuleInterface/Resources.resx new file mode 100644 index 0000000000..3e27759bfd --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/Resources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Paste As Plain Text + + + PowerToys integration to paste clipboard contents as plain text + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp b/src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp new file mode 100644 index 0000000000..ec4f44c1c4 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/dllmain.cpp @@ -0,0 +1,518 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" + +#include +#include "trace.h" +#include "Generated Files/resource.h" +#include +#include +#include + +#include "PastePlainConstants.h" +#include +#include +#include + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, + DWORD ul_reason_for_call, + LPVOID /*lpReserved*/) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + + return TRUE; +} + +namespace +{ + const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; + const wchar_t JSON_KEY_WIN[] = L"win"; + const wchar_t JSON_KEY_ALT[] = L"alt"; + const wchar_t JSON_KEY_CTRL[] = L"ctrl"; + const wchar_t JSON_KEY_SHIFT[] = L"shift"; + const wchar_t JSON_KEY_CODE[] = L"code"; + const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut"; +} + +struct ModuleSettings +{ +} g_settings; + +class PastePlain : public PowertoyModuleIface +{ +private: + bool m_enabled = false; + + std::wstring app_name; + + //contains the non localized key of the powertoy + std::wstring app_key; + + HANDLE m_hProcess; + + // Time to wait for process to close after sending WM_CLOSE signal + static const int MAX_WAIT_MILLISEC = 10000; + + Hotkey m_hotkey; + + // Handle to event used to invoke PastePlain + HANDLE m_hInvokeEvent; + + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) + { + auto settingsObject = settings.get_raw_json(); + if (settingsObject.GetView().Size()) + { + try + { + auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT); + m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN); + m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT); + m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT); + m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL); + m_hotkey.key = static_cast(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE)); + } + catch (...) + { + Logger::error("Failed to initialize PastePlain start shortcut"); + } + } + else + { + Logger::info("PastePlain settings are empty"); + } + + if (!m_hotkey.key) + { + Logger::info("PastePlain is going to use default shortcut"); + m_hotkey.win = true; + m_hotkey.alt = true; + m_hotkey.shift = false; + m_hotkey.ctrl = true; + m_hotkey.key = 'V'; + } + } + + bool is_process_running() + { + return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; + } + + void launch_process() + { + Logger::trace(L"Starting PastePlain process"); + unsigned long powertoys_pid = GetCurrentProcessId(); + + std::wstring executable_args = L""; + executable_args.append(std::to_wstring(powertoys_pid)); + + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = L"modules\\PastePlain\\PowerToys.PastePlain.exe"; + sei.nShow = SW_SHOWNORMAL; + sei.lpParameters = executable_args.data(); + if (ShellExecuteExW(&sei)) + { + Logger::trace("Successfully started the PastePlain process"); + } + else + { + Logger::error(L"PastePlain failed to start. {}", get_last_error_or_default(GetLastError())); + } + + m_hProcess = sei.hProcess; + } + + // Load the settings file. + void init_settings() + { + try + { + // Load and parse the settings file for this PowerToy. + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(get_key()); + + parse_hotkey(settings); + } + catch (std::exception&) + { + Logger::warn(L"An exception occurred while loading the settings file"); + // Error while loading from the settings file. Let default values stay as they are. + } + } + + void try_inject_modifier_key_up(std::vector& inputs, short modifier) + { + // Most significant bit is set if key is down + if ((GetAsyncKeyState(static_cast(modifier)) & 0x8000) != 0) + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = modifier; + input_event.ki.dwFlags = KEYEVENTF_KEYUP; + inputs.push_back(input_event); + } + } + + void try_inject_modifier_key_restore(std::vector &inputs, short modifier) + { + // Most significant bit is set if key is down + if ((GetAsyncKeyState(static_cast(modifier)) & 0x8000) != 0) + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = modifier; + inputs.push_back(input_event); + } + } + + void try_to_paste_as_plain_text() + { + std::wstring clipboard_text; + + { + // Read clipboard data begin + + if (!OpenClipboard(NULL)) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't open the clipboard to get the text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.OpenClipboard"); + return; + } + HANDLE h_clipboard_data = GetClipboardData(CF_UNICODETEXT); + + if (h_clipboard_data == NULL) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Failed to get clipboard data. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GetClipboardData"); + CloseClipboard(); + return; + } + + wchar_t* pch_data = static_cast(GlobalLock(h_clipboard_data)); + + if (NULL == pch_data) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't lock the buffer to get the unformatted text from the clipboard. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GlobalLock"); + CloseClipboard(); + return; + } + + clipboard_text = pch_data; + GlobalUnlock(h_clipboard_data); + + CloseClipboard(); + // Read clipboard data end + } + + { + // Copy text to clipboard begin + UINT no_clipboard_history_or_roaming_format = 0; + + // Get the format identifier for not adding the data to the clipboard history or roaming. + // https://learn.microsoft.com/windows/win32/dataxchg/clipboard-formats#cloud-clipboard-and-clipboard-history-formats + if (0 == (no_clipboard_history_or_roaming_format = RegisterClipboardFormat(L"ExcludeClipboardContentFromMonitorProcessing"))) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't get the clipboard data format type that would allow excluding the data from the clipboard history / roaming. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.RegisterClipboardFormat"); + return; + } + + if (!OpenClipboard(NULL)) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't open the clipboard to copy the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.OpenClipboard"); + return; + } + + HGLOBAL h_clipboard_data; + + if (NULL == (h_clipboard_data = GlobalAlloc(GMEM_MOVEABLE, (clipboard_text.length() + 1) * sizeof(wchar_t)))) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't allocate a buffer for the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalAlloc"); + CloseClipboard(); + return; + } + wchar_t* pch_data = static_cast(GlobalLock(h_clipboard_data)); + + if (NULL == pch_data) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't lock the buffer to send the unformatted text to the clipboard. {}", errorMessage.has_value() ? errorMessage.value() : L""); + GlobalFree(h_clipboard_data); + CloseClipboard(); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalLock"); + return; + } + + wcscpy_s(pch_data, clipboard_text.length() + 1, clipboard_text.c_str()); + + EmptyClipboard(); + + if (NULL == SetClipboardData(CF_UNICODETEXT, h_clipboard_data)) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"Couldn't set the clipboard data to the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L""); + GlobalUnlock(h_clipboard_data); + GlobalFree(h_clipboard_data); + CloseClipboard(); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.SetClipboardData"); + return; + } + + // Don't show in history or allow data roaming. + SetClipboardData(no_clipboard_history_or_roaming_format, 0); + + CloseClipboard(); + // Copy text to clipboard end + } + { + // Clear kb state and send Ctrl+V begin + + // we can assume that the last pressed key is... + // (1) not a modifier key and + // (2) marked as handled (so it never gets a key down input event). + // So, let's check which modifiers were pressed, + // and, if they were, inject a key up event for each of them + std::vector inputs; + try_inject_modifier_key_up(inputs, VK_LCONTROL); + try_inject_modifier_key_up(inputs, VK_RCONTROL); + try_inject_modifier_key_up(inputs, VK_LWIN); + try_inject_modifier_key_up(inputs, VK_RWIN); + try_inject_modifier_key_up(inputs, VK_LSHIFT); + try_inject_modifier_key_up(inputs, VK_RSHIFT); + try_inject_modifier_key_up(inputs, VK_LMENU); + try_inject_modifier_key_up(inputs, VK_RMENU); + + // send Ctrl+V (key downs and key ups) + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = VK_CONTROL; + inputs.push_back(input_event); + } + + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = 0x56; // V + // Avoid triggering detection by the centralized keyboard hook. Allows using Control+V as activation. + input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG; + inputs.push_back(input_event); + } + + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = 0x56; // V + input_event.ki.dwFlags = KEYEVENTF_KEYUP; + // Avoid triggering detection by the centralized keyboard hook. Allows using Control+V as activation. + input_event.ki.dwExtraInfo = CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG; + inputs.push_back(input_event); + } + + { + INPUT input_event = {}; + input_event.type = INPUT_KEYBOARD; + input_event.ki.wVk = VK_CONTROL; + input_event.ki.dwFlags = KEYEVENTF_KEYUP; + inputs.push_back(input_event); + } + + try_inject_modifier_key_restore(inputs, VK_LCONTROL); + try_inject_modifier_key_restore(inputs, VK_RCONTROL); + try_inject_modifier_key_restore(inputs, VK_LWIN); + try_inject_modifier_key_restore(inputs, VK_RWIN); + try_inject_modifier_key_restore(inputs, VK_LSHIFT); + try_inject_modifier_key_restore(inputs, VK_RSHIFT); + try_inject_modifier_key_restore(inputs, VK_LMENU); + try_inject_modifier_key_restore(inputs, VK_RMENU); + + auto uSent = SendInput(static_cast(inputs.size()), inputs.data(), sizeof(INPUT)); + if (uSent != inputs.size()) + { + DWORD errorCode = GetLastError(); + auto errorMessage = get_last_error_message(errorCode); + Logger::error(L"SendInput failed. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L""); + Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput"); + return; + } + + // Clear kb state and send Ctrl+V end + } + Trace::PastePlainSuccess(); + } + +public: + PastePlain() + { + app_name = GET_RESOURCE_STRING(IDS_PASTEPLAIN_NAME); + app_key = PastePlainConstants::ModuleKey; + LoggerHelpers::init_logger(app_key, L"ModuleInterface", "PastePlain"); + init_settings(); + } + + ~PastePlain() + { + if (m_enabled) + { + } + m_enabled = false; + } + + // Destroy the powertoy and free memory + virtual void destroy() override + { + Logger::trace("PastePlain::destroy()"); + delete this; + } + + // Return the localized display name of the powertoy + virtual const wchar_t* get_name() override + { + return app_name.c_str(); + } + + // Return the non localized key of the powertoy, this will be cached by the runner + virtual const wchar_t* get_key() override + { + return app_key.c_str(); + } + + // Return the configured status for the gpo policy for the module + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredPastePlainEnabledValue(); + } + + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(GET_RESOURCE_STRING(IDS_PASTEPLAIN_SETTINGS_DESC)); + + settings.set_overview_link(L"https://aka.ms/PowerToysOverview_PastePlain"); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + virtual void call_custom_action(const wchar_t* /*action*/) override + { + } + + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + parse_hotkey(values); + // If you don't need to do any custom processing of the settings, proceed + // to persists the values calling: + values.save_to_settings_file(); + // Otherwise call a custom function to process the settings before saving them to disk: + // save_settings(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + virtual void enable() + { + Logger::trace("PastePlain::enable()"); + m_enabled = true; + Trace::EnablePastePlain(true); + }; + + virtual void disable() + { + Logger::trace("PastePlain::disable()"); + m_enabled = false; + Trace::EnablePastePlain(false); + } + + virtual bool on_hotkey(size_t /*hotkeyId*/) override + { + if (m_enabled) + { + Logger::trace(L"PastePlain hotkey pressed"); + + std::thread([=]() { + // hotkey work should be kept to a minimum, or Windows might deregister our low level keyboard hook. + // Move work to another thread. + try_to_paste_as_plain_text(); + }).detach(); + + Trace::PastePlainInvoked(); + return true; + } + + return false; + } + + virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override + { + if (m_hotkey.key) + { + if (hotkeys && buffer_size >= 1) + { + hotkeys[0] = m_hotkey; + } + + return 1; + } + else + { + return 0; + } + } + + virtual bool is_enabled() override + { + return m_enabled; + } + + virtual void send_settings_telemetry() override + { + Logger::info("Send settings telemetry"); + Trace::SettingsTelemetry(m_hotkey); + } +}; + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new PastePlain(); +} diff --git a/src/modules/pasteplain/PastePlainModuleInterface/packages.config b/src/modules/pasteplain/PastePlainModuleInterface/packages.config new file mode 100644 index 0000000000..48319b8c95 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/pch.cpp b/src/modules/pasteplain/PastePlainModuleInterface/pch.cpp new file mode 100644 index 0000000000..ce9b73991b --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/pch.cpp @@ -0,0 +1,2 @@ +#include "pch.h" + diff --git a/src/modules/pasteplain/PastePlainModuleInterface/pch.h b/src/modules/pasteplain/PastePlainModuleInterface/pch.h new file mode 100644 index 0000000000..eddac0fdc1 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/pch.h @@ -0,0 +1,7 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/src/modules/pasteplain/PastePlainModuleInterface/resource.base.h b/src/modules/pasteplain/PastePlainModuleInterface/resource.base.h new file mode 100644 index 0000000000..e2a93202be --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/resource.base.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PowerOCR.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys PastePlain" +#define INTERNAL_NAME "PowerToys.PastePlainModuleInterface" +#define ORIGINAL_FILENAME "PowerToys.PastePlainModuleInterface.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/pasteplain/PastePlainModuleInterface/trace.cpp b/src/modules/pasteplain/PastePlainModuleInterface/trace.cpp new file mode 100644 index 0000000000..a6f06b1ca9 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/trace.cpp @@ -0,0 +1,82 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +// Log if the user has PastePlain enabled or disabled +void Trace::EnablePastePlain(const bool enabled) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_EnablePastePlain", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingBoolean(enabled, "Enabled")); +} + +// Log if the user has invoked PastePlain +void Trace::PastePlainInvoked() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_InvokePastePlain", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +// Log if a PastePlain invocation has succeeded +void Trace::PastePlainSuccess() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_Success", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} + +// Log if an error occurs in PastePlain +void Trace::PastePlainError(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "PastePlain_Error", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingValue(methodName.c_str(), "MethodName"), + TraceLoggingValue(errorCode, "ErrorCode"), + TraceLoggingValue(errorMessage.c_str(), "ErrorMessage")); +} + +// Event to send settings telemetry. +void Trace::SettingsTelemetry(PowertoyModuleIface::Hotkey& hotkey) noexcept +{ + std::wstring hotKeyStr = + std::wstring(hotkey.win ? L"Win + " : L"") + + std::wstring(hotkey.ctrl ? L"Ctrl + " : L"") + + std::wstring(hotkey.shift ? L"Shift + " : L"") + + std::wstring(hotkey.alt ? L"Alt + " : L"") + + std::wstring(L"VK ") + std::to_wstring(hotkey.key); + + TraceLoggingWrite( + g_hProvider, + "PastePlain_Settings", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingWideString(hotKeyStr.c_str(), "HotKey") + ); +} diff --git a/src/modules/pasteplain/PastePlainModuleInterface/trace.h b/src/modules/pasteplain/PastePlainModuleInterface/trace.h new file mode 100644 index 0000000000..6b65d0f010 --- /dev/null +++ b/src/modules/pasteplain/PastePlainModuleInterface/trace.h @@ -0,0 +1,24 @@ +#pragma once +#include + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + + // Log if the user has PastePlain enabled or disabled + static void EnablePastePlain(const bool enabled) noexcept; + + // Log if the user has invoked PastePlain + static void PastePlainInvoked() noexcept; + + // Log if a PastePlain invocation has succeeded + static void Trace::PastePlainSuccess() noexcept; + + // Log if an error occurs in PastePlain + static void Trace::PastePlainError(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept; + + // Event to send settings telemetry. + static void Trace::SettingsTelemetry(PowertoyModuleIface::Hotkey& hotkey) noexcept; +}; diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index 2cfd8e1f0a..6521fc9c73 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using PowerToys.PowerAccentKeyboardService; -using Windows.Globalization; namespace PowerAccent.Core { @@ -26,9 +24,11 @@ namespace PowerAccent.Core IS, IT, KU, + LT, MK, MI, NL, + NO, PI, PL, PT, @@ -40,7 +40,7 @@ namespace PowerAccent.Core TK, } - internal class Languages + internal sealed class Languages { public static string[] GetDefaultLetterKey(LetterKey letter, Language lang) { @@ -51,7 +51,7 @@ namespace PowerAccent.Core Language.CUR => GetDefaultLetterKeyCUR(letter), // Currency Language.CY => GetDefaultLetterKeyCY(letter), // Welsh Language.CZ => GetDefaultLetterKeyCZ(letter), // Czech - Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish Gaelic) + Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish) Language.GD => GetDefaultLetterKeyGD(letter), // Gàidhlig (Scottish Gaelic) Language.DE => GetDefaultLetterKeyDE(letter), // German Language.EST => GetDefaultLetterKeyEST(letter), // Estonian @@ -62,9 +62,11 @@ namespace PowerAccent.Core Language.IS => GetDefaultLetterKeyIS(letter), // Iceland Language.IT => GetDefaultLetterKeyIT(letter), // Italian Language.KU => GetDefaultLetterKeyKU(letter), // Kurdish + Language.LT => GetDefaultLetterKeyLT(letter), // Lithuanian Language.MK => GetDefaultLetterKeyMK(letter), // Macedonian Language.MI => GetDefaultLetterKeyMI(letter), // Maori Language.NL => GetDefaultLetterKeyNL(letter), // Dutch + Language.NO => GetDefaultLetterKeyNO(letter), // Norwegian Language.PI => GetDefaultLetterKeyPI(letter), // Pinyin Language.PL => GetDefaultLetterKeyPL(letter), // Polish Language.PT => GetDefaultLetterKeyPT(letter), // Portuguese @@ -93,7 +95,7 @@ namespace PowerAccent.Core LetterKey.VK_7 => new string[] { "₇", "⁷" }, LetterKey.VK_8 => new string[] { "₈", "⁸" }, LetterKey.VK_9 => new string[] { "₉", "⁹" }, - LetterKey.VK_A => new string[] { "á", "à", "ä", "â", "ă", "å", "α", "ā", "ą", "ȧ", "ã", "æ" }, + LetterKey.VK_A => new string[] { "á", "à", "ä", "â", "ă", "å", "α", "ā", "ą", "ȧ", "ã", "ǎ", "æ" }, LetterKey.VK_B => new string[] { "ḃ", "β" }, LetterKey.VK_C => new string[] { "ç", "ć", "ĉ", "č", "ċ", "¢", "χ" }, LetterKey.VK_D => new string[] { "ď", "ḋ", "đ", "δ", "ð" }, @@ -101,18 +103,18 @@ namespace PowerAccent.Core LetterKey.VK_F => new string[] { "ƒ", "ḟ" }, LetterKey.VK_G => new string[] { "ğ", "ģ", "ǧ", "ġ", "ĝ", "ǥ", "γ" }, LetterKey.VK_H => new string[] { "ḣ", "ĥ", "ħ" }, - LetterKey.VK_I => new string[] { "ï", "î", "í", "ì", "ī", "į", "i", "ı", "İ", "ι" }, + LetterKey.VK_I => new string[] { "ï", "î", "í", "ì", "ī", "į", "ı", "İ", "ι", "ǐ" }, LetterKey.VK_J => new string[] { "ĵ" }, LetterKey.VK_K => new string[] { "ķ", "ǩ", "κ" }, LetterKey.VK_L => new string[] { "ĺ", "ľ", "ļ", "ł", "₺", "λ" }, LetterKey.VK_M => new string[] { "ṁ", "μ" }, LetterKey.VK_N => new string[] { "ñ", "ń", "ŋ", "ň", "ņ", "ṅ", "ⁿ", "ν" }, - LetterKey.VK_O => new string[] { "ô", "ó", "ö", "ő", "ò", "ō", "ȯ", "ø", "õ", "œ", "ω", "ο" }, + LetterKey.VK_O => new string[] { "ô", "ó", "ö", "ő", "ò", "ō", "ȯ", "ø", "õ", "œ", "ω", "ο", "ǒ" }, LetterKey.VK_P => new string[] { "ṗ", "₽", "π", "φ", "ψ" }, LetterKey.VK_R => new string[] { "ŕ", "ř", "ṙ", "₹", "ρ" }, LetterKey.VK_S => new string[] { "ś", "ş", "š", "ș", "ṡ", "ŝ", "ß", "σ", "$" }, LetterKey.VK_T => new string[] { "ţ", "ť", "ț", "ṫ", "ŧ", "θ", "τ", "þ" }, - LetterKey.VK_U => new string[] { "û", "ú", "ü", "ŭ", "ű", "ù", "ů", "ū", "ų", "υ" }, + LetterKey.VK_U => new string[] { "û", "ú", "ü", "ŭ", "ű", "ù", "ů", "ū", "ų", "ǔ", "υ", "ǖ", "ǘ", "ǚ", "ǜ" }, LetterKey.VK_W => new string[] { "ẇ", "ŵ", "₩" }, LetterKey.VK_X => new string[] { "ẋ", "ξ" }, LetterKey.VK_Y => new string[] { "ÿ", "ŷ", "ý", "ẏ" }, @@ -280,23 +282,23 @@ namespace PowerAccent.Core { return letter switch { - LetterKey.VK_1 => new string[] { "̄", "ˉ", "1" }, - LetterKey.VK_2 => new string[] { "́", "ˊ", "2" }, - LetterKey.VK_3 => new string[] { "̌", "ˇ", "3" }, - LetterKey.VK_4 => new string[] { "̀", "ˋ", "4" }, - LetterKey.VK_5 => new string[] { "˙", "5" }, - LetterKey.VK_A => new string[] { "ā", "á", "ǎ", "à", "a", "ɑ", "ɑ̄", "ɑ́", "ɑ̌", "ɑ̀" }, - LetterKey.VK_C => new string[] { "ĉ", "c" }, - LetterKey.VK_E => new string[] { "ē", "é", "ě", "è", "e", "ê", "ê̄", "ế", "ê̌", "ề" }, - LetterKey.VK_I => new string[] { "ī", "í", "ǐ", "ì", "i" }, - LetterKey.VK_M => new string[] { "m̄", "ḿ", "m̌", "m̀", "m" }, - LetterKey.VK_N => new string[] { "n̄", "ń", "ň", "ǹ", "n", "ŋ", "ŋ̄", "ŋ́", "ŋ̌", "ŋ̀" }, - LetterKey.VK_O => new string[] { "ō", "ó", "ǒ", "ò", "o" }, - LetterKey.VK_S => new string[] { "ŝ", "s" }, - LetterKey.VK_U => new string[] { "ū", "ú", "ǔ", "ù", "u", "ü", "ǖ", "ǘ", "ǚ", "ǜ" }, - LetterKey.VK_V => new string[] { "ǖ", "ǘ", "ǚ", "ǜ", "ü" }, - LetterKey.VK_Y => new string[] { "¥", "y" }, - LetterKey.VK_Z => new string[] { "ẑ", "z" }, + LetterKey.VK_1 => new string[] { "\u0304", "ˉ" }, + LetterKey.VK_2 => new string[] { "\u0301", "ˊ" }, + LetterKey.VK_3 => new string[] { "\u030c", "ˇ" }, + LetterKey.VK_4 => new string[] { "\u0300", "ˋ" }, + LetterKey.VK_5 => new string[] { "·" }, + LetterKey.VK_A => new string[] { "ā", "á", "ǎ", "à", "ɑ", "ɑ\u0304", "ɑ\u0301", "ɑ\u030c", "ɑ\u0300" }, + LetterKey.VK_C => new string[] { "ĉ" }, + LetterKey.VK_E => new string[] { "ē", "é", "ě", "è", "ê", "ê\u0304", "ế", "ê\u030c", "ề" }, + LetterKey.VK_I => new string[] { "ī", "í", "ǐ", "ì" }, + LetterKey.VK_M => new string[] { "m\u0304", "ḿ", "m\u030c", "m\u0300" }, + LetterKey.VK_N => new string[] { "n\u0304", "ń", "ň", "ǹ", "ŋ", "ŋ\u0304", "ŋ\u0301", "ŋ\u030c", "ŋ\u0300" }, + LetterKey.VK_O => new string[] { "ō", "ó", "ǒ", "ò" }, + LetterKey.VK_S => new string[] { "ŝ" }, + LetterKey.VK_U => new string[] { "ū", "ú", "ǔ", "ù", "ü", "ǖ", "ǘ", "ǚ", "ǜ" }, + LetterKey.VK_V => new string[] { "ü", "ǖ", "ǘ", "ǚ", "ǜ" }, + LetterKey.VK_Y => new string[] { "¥" }, + LetterKey.VK_Z => new string[] { "ẑ" }, _ => Array.Empty(), }; } @@ -456,20 +458,20 @@ namespace PowerAccent.Core { return letter switch { - LetterKey.VK_A => new string[] { "שׂ", "שׁ", "ְ" }, + LetterKey.VK_A => new string[] { "שׂ", "שׁ", "\u05b0" }, LetterKey.VK_B => new string[] { "׆" }, - LetterKey.VK_E => new string[] { "ָ", "ֳ", "ֻ" }, + LetterKey.VK_E => new string[] { "\u05b8", "\u05b3", "\u05bb" }, LetterKey.VK_G => new string[] { "ױ" }, - LetterKey.VK_H => new string[] { "ײ", "ײַ", "ׯ", "ִ" }, - LetterKey.VK_M => new string[] { "ֵ" }, - LetterKey.VK_P => new string[] { "ַ", "ֲ" }, - LetterKey.VK_S => new string[] { "ּ" }, + LetterKey.VK_H => new string[] { "ײ", "ײַ", "ׯ", "\u05b4" }, + LetterKey.VK_M => new string[] { "\u05b5" }, + LetterKey.VK_P => new string[] { "\u05b7", "\u05b2" }, + LetterKey.VK_S => new string[] { "\u05bc" }, LetterKey.VK_T => new string[] { "ﭏ" }, - LetterKey.VK_U => new string[] { "וֹ", "וּ", "װ", "ֹ" }, - LetterKey.VK_X => new string[] { "ֶ", "ֱ" }, + LetterKey.VK_U => new string[] { "וֹ", "וּ", "װ", "\u05b9" }, + LetterKey.VK_X => new string[] { "\u05b6", "\u05b1" }, LetterKey.VK_Y => new string[] { "ױ" }, LetterKey.VK_COMMA => new string[] { "”", "’", "״", "׳" }, - LetterKey.VK_PERIOD => new string[] { "֫", "ֽ", "ֿ" }, + LetterKey.VK_PERIOD => new string[] { "\u05ab", "\u05bd", "\u05bf" }, LetterKey.VK_MINUS => new string[] { "–", "־" }, _ => Array.Empty(), }; @@ -583,5 +585,34 @@ namespace PowerAccent.Core _ => Array.Empty(), }; } + + // Norwegian + private static string[] GetDefaultLetterKeyNO(LetterKey letter) + { + return letter switch + { + LetterKey.VK_A => new string[] { "å", "æ" }, + LetterKey.VK_E => new string[] { "€" }, + LetterKey.VK_O => new string[] { "ø" }, + LetterKey.VK_S => new string[] { "$" }, + _ => Array.Empty(), + }; + } + + // Lithuanian + private static string[] GetDefaultLetterKeyLT(LetterKey letter) + { + return letter switch + { + LetterKey.VK_A => new string[] { "ą" }, + LetterKey.VK_C => new string[] { "č" }, + LetterKey.VK_E => new string[] { "ę", "ė", "€" }, + LetterKey.VK_I => new string[] { "į" }, + LetterKey.VK_S => new string[] { "š" }, + LetterKey.VK_U => new string[] { "ų", "ū" }, + LetterKey.VK_Z => new string[] { "ž" }, + _ => Array.Empty(), + }; + } } } diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj index a622b1d653..49220b81a6 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.Core.csproj @@ -26,15 +26,16 @@ - - - - - + + + + + + diff --git a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs index 999620ac32..01d87d034c 100644 --- a/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs +++ b/src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Text; using System.Unicode; using System.Windows; +using ManagedCommon; using PowerAccent.Core.Services; using PowerAccent.Core.Tools; using PowerToys.PowerAccentKeyboardService; @@ -41,6 +42,8 @@ public class PowerAccent : IDisposable public PowerAccent() { + Logger.InitializeLogger("\\QuickAccent\\Logs"); + LoadUnicodeInfoCache(); _keyboardListener = new KeyboardListener(); diff --git a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs index d3d89821c9..50ba68f546 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs @@ -4,6 +4,7 @@ using System.IO.Abstractions; using System.Text.Json; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs b/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs index 6dec635eac..1429be6f34 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Tools/CharactersUsageInfo.cs @@ -34,6 +34,7 @@ namespace PowerAccent.Core.Tools return timestamp; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1854:Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method", Justification = "False positive: https://github.com/dotnet/roslyn-analyzers/issues/6390")] public void IncrementUsageFrequency(string character) { if (_characterUsageCounters.ContainsKey(character)) diff --git a/src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs b/src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs deleted file mode 100644 index f79e3a42cc..0000000000 --- a/src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs +++ /dev/null @@ -1,78 +0,0 @@ -// 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. - -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.IO.Abstractions; -using interop; - -namespace PowerAccent.Core.Tools -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "QuickAccent\\Logs"); - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - string logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - System.Reflection.MethodBase methodName = stackTrace.GetFrame(3)?.GetMethod(); - string className = methodName?.DeclaringType?.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs b/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs index e4c77b9d0b..af3ff38db9 100644 --- a/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs +++ b/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Threading; using System.Windows; using Common.UI; +using ManagedCommon; using PowerAccent.Core.Tools; namespace PowerAccent.UI diff --git a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj index cc01326236..65e002bfd5 100644 --- a/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj +++ b/src/modules/poweraccent/PowerAccent.UI/PowerAccent.UI.csproj @@ -32,12 +32,13 @@ - - + + + diff --git a/src/modules/poweraccent/PowerAccent.UI/Program.cs b/src/modules/poweraccent/PowerAccent.UI/Program.cs index eee06b2038..9ca3de95be 100644 --- a/src/modules/poweraccent/PowerAccent.UI/Program.cs +++ b/src/modules/poweraccent/PowerAccent.UI/Program.cs @@ -22,6 +22,8 @@ internal static class Program [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\QuickAccent\\Logs"); + if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredQuickAccentEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); diff --git a/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs b/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs index d243c3f20d..f83e20c037 100644 --- a/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs +++ b/src/modules/poweraccent/PowerAccent.UI/Selector.xaml.cs @@ -17,7 +17,7 @@ public partial class Selector : Window, IDisposable, INotifyPropertyChanged private Visibility _characterNameVisibility = Visibility.Visible; - private int _selectedIndex = 0; + private int _selectedIndex; public event PropertyChangedEventHandler PropertyChanged; diff --git a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp index 6c48ae0a04..79aee387fc 100644 --- a/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp +++ b/src/modules/poweraccent/PowerAccentKeyboardService/KeyboardListener.cpp @@ -94,7 +94,7 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation { std::vector excludedApps; auto excludedUppercase = std::wstring(excludedAppsView); - CharUpperBuffW(excludedUppercase.data(), (DWORD)excludedUppercase.length()); + CharUpperBuffW(excludedUppercase.data(), static_cast(excludedUppercase.length())); std::wstring_view view(excludedUppercase); view = left_trim(trim(view)); @@ -129,7 +129,7 @@ namespace winrt::PowerToys::PowerAccentKeyboardService::implementation return m_prevForegroundAppExcl.second; } auto processPath = get_process_path(foregroundApp); - CharUpperBuffW(processPath.data(), (DWORD)processPath.length()); + CharUpperBuffW(processPath.data(), static_cast(processPath.length())); m_prevForegroundAppExcl = { foregroundApp, find_app_name_in_path(processPath, m_settings.excludedApps) }; diff --git a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp index c597e53f34..028dc2c31b 100644 --- a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp +++ b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp @@ -93,12 +93,6 @@ public: { *cmdState = ECS_ENABLED; - // We've observed that it's possible that a null gets passed instead of an empty array. Just don't show the context menu in this case. - if (nullptr == selection) { - *cmdState = ECS_HIDDEN; - return S_OK; - } - if (!CSettingsInstance().GetEnabled()) { *cmdState = ECS_HIDDEN; @@ -112,6 +106,12 @@ public: return S_OK; } + // When right clicking directory background, selection is empty. This prevents checking if there + // are renamable items, but internal PowerRename logic will prevent renaming non-renamable items anyway. + if (nullptr == selection) { + return S_OK; + } + // Check if at least one of the selected items is actually renamable. if (!ShellItemArrayContainsRenamableItem(selection)) { @@ -213,7 +213,7 @@ private: auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L""); } - else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK) + else if (UuidToString(&temp_uuid, reinterpret_cast(& uuid_chars)) != RPC_S_OK) { auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L""); @@ -222,7 +222,7 @@ private: if (uuid_chars != nullptr) { pipe_name += std::wstring(uuid_chars); - RpcStringFree((RPC_WSTR*)&uuid_chars); + RpcStringFree(reinterpret_cast(&uuid_chars)); uuid_chars = nullptr; } create_pipe_thread = std::thread(&PowerRenameContextMenuCommand::StartNamedPipeServerAndSendData, this, pipe_name); diff --git a/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp index f76b8018bf..48cefa7e37 100644 --- a/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp +++ b/src/modules/powerrename/PowerRenameUILib/ExplorerItem.cpp @@ -164,18 +164,20 @@ namespace winrt::PowerRenameUI::implementation std::wstring ExplorerItem::StateToErrorMessage() { - auto factory = winrt::get_activation_factory(); - ResourceManager manager = factory.CreateInstance(L"resources.pri"); + static auto factory = winrt::get_activation_factory(); + static ResourceManager manager = factory.CreateInstance(L"resources.pri"); + static auto invalid_char_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_InvalidChar").ValueAsString(); + static auto name_too_long_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_FileNameTooLong").ValueAsString(); switch (m_state) { case PowerRenameItemRenameStatus::ItemNameInvalidChar: { - return std::wstring{ manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_InvalidChar").ValueAsString() }; + return std::wstring{ invalid_char_error }; } case PowerRenameItemRenameStatus::ItemNameTooLong: { - return std::wstring{ manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_FileNameTooLong").ValueAsString() }; + return std::wstring{ name_too_long_error }; } default: return {}; diff --git a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp index 9e9176b7e3..e520eb321f 100644 --- a/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp +++ b/src/modules/powerrename/PowerRenameUILib/MainWindow.xaml.cpp @@ -87,8 +87,8 @@ namespace winrt::PowerRenameUI::implementation winrt::Windows::Graphics::RectInt32 rect; // Scale window size - rect.Width = (int32_t)(width * (float)window_dpi / x_dpi); - rect.Height = (int32_t)(height * (float)window_dpi / x_dpi); + rect.Width = static_cast(width * static_cast(window_dpi) / x_dpi); + rect.Height = static_cast(height * static_cast(window_dpi) / x_dpi); // Center to screen rect.X = displayArea.WorkArea().X + displayArea.WorkArea().Width / 2 - width / 2; rect.Y = displayArea.WorkArea().Y + displayArea.WorkArea().Height / 2 - height / 2; @@ -273,6 +273,13 @@ namespace winrt::PowerRenameUI::implementation { ToggleAll(); m_allSelected = !m_allSelected; + if (button_showRenamed().IsChecked()) + { + m_explorerItems.Clear(); + m_explorerItemsMap.clear(); + PopulateExplorerItems(); + UpdateCounts(); + } } } @@ -738,7 +745,8 @@ namespace winrt::PowerRenameUI::implementation int id = 0; spItem->GetId(&id); auto item = FindById(id); - item.Checked(selected); + if (item) + item.Checked(selected); } } UpdateCounts(); @@ -1070,7 +1078,7 @@ namespace winrt::PowerRenameUI::implementation if (closeUIWindowAfterRenaming) { // Close the window - PostMessage(m_window, WM_CLOSE, (WPARAM)0, (LPARAM)0); + PostMessage(m_window, WM_CLOSE, static_cast(0), static_cast(0)); } else { diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index 3b8b0c4697..3e6a47e706 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -1,7 +1,7 @@  + - true @@ -185,8 +185,8 @@ - + @@ -197,10 +197,10 @@ - - + + diff --git a/src/modules/powerrename/PowerRenameUILib/app.manifest b/src/modules/powerrename/PowerRenameUILib/app.manifest index 0066894996..8554f867e0 100644 --- a/src/modules/powerrename/PowerRenameUILib/app.manifest +++ b/src/modules/powerrename/PowerRenameUILib/app.manifest @@ -12,4 +12,10 @@ PerMonitorV2, PerMonitor + + + + + + diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config index 485d1bab40..78191badcf 100644 --- a/src/modules/powerrename/PowerRenameUILib/packages.config +++ b/src/modules/powerrename/PowerRenameUILib/packages.config @@ -5,5 +5,5 @@ - + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUILib/pch.h b/src/modules/powerrename/PowerRenameUILib/pch.h index a5ed89774f..95f939500a 100644 --- a/src/modules/powerrename/PowerRenameUILib/pch.h +++ b/src/modules/powerrename/PowerRenameUILib/pch.h @@ -13,7 +13,13 @@ #include #include #include + +// disable warning 26493 - Don't use C-style casts +#pragma warning(push) +#pragma warning(disable: 26493) #include +#pragma warning(pop) + #include #include #include diff --git a/src/modules/powerrename/dll/PowerRenameExt.cpp b/src/modules/powerrename/dll/PowerRenameExt.cpp index cd0346b246..95b42089a4 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.cpp +++ b/src/modules/powerrename/dll/PowerRenameExt.cpp @@ -46,14 +46,26 @@ HRESULT CPowerRenameMenu::s_CreateInstance(_In_opt_ IUnknown*, _In_ REFIID riid, } // IShellExtInit -HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject* pdtobj, HKEY) +HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE idlist, _In_ IDataObject* pdtobj, HKEY) { // Check if we have disabled ourselves if (!CSettingsInstance().GetEnabled()) return E_FAIL; // Cache the data object to be used later - m_spdo = pdtobj; + if (idlist != NULL) + { + CComPtr spsia; + if (SUCCEEDED(SHCreateShellItemArrayFromIDLists(1, &idlist, &spsia)) && spsia != NULL) + { + spsia->BindToHandler(NULL, BHID_DataObject, IID_IDataObject, reinterpret_cast(&m_spdo)); + } + } + else + { + m_spdo = pdtobj; + } + return S_OK; } @@ -88,7 +100,7 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs if (CSettingsInstance().GetShowIconOnMenu()) { - HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0); + HICON hIcon = static_cast(LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0)); if (hIcon) { mii.fMask |= MIIM_BITMAP; @@ -124,14 +136,14 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr HRESULT hr = E_FAIL; if (CSettingsInstance().GetEnabled() && - (IS_INTRESOURCE(pici->lpVerb)) && + pici && (IS_INTRESOURCE(pici->lpVerb)) && (LOWORD(pici->lpVerb) == 0)) { Trace::Invoked(); // Set the application path based on the location of the dll std::wstring path = get_module_folderpath(g_hInst); path = path + L"\\PowerToys.PowerRename.exe"; - LPTSTR lpApplicationName = (LPTSTR)path.c_str(); + LPTSTR lpApplicationName = path.data(); // Create an anonymous pipe to stream filenames SECURITY_ATTRIBUTES sa; HANDLE hReadPipe; @@ -163,14 +175,7 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr startupInfo.cb = sizeof(STARTUPINFO); startupInfo.hStdInput = hReadPipe; startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - if (pici) - { - startupInfo.wShowWindow = static_cast(pici->nShow); - } - else - { - startupInfo.wShowWindow = SW_SHOWNORMAL; - } + startupInfo.wShowWindow = static_cast(pici->nShow); PROCESS_INFORMATION processInformation; @@ -201,15 +206,18 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr // psiItemArray is NULL if called from InvokeCommand. This part is used for the MSI installer. It is not NULL if it is called from Invoke (MSIX). if (!psiItemArray) { - // Stream the input files - HDropIterator i(m_spdo); - for (i.First(); !i.IsDone(); i.Next()) + if (m_spdo) { - CString fileName(i.CurrentItem()); - // File name can't contain '?' - fileName.Append(_T("?")); + // Stream the input files + HDropIterator i(m_spdo); + for (i.First(); !i.IsDone(); i.Next()) + { + CString fileName(i.CurrentItem()); + // File name can't contain '?' + fileName.Append(_T("?")); - writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR)); + writePipe.Write(fileName, fileName.GetLength() * sizeof(TCHAR)); + } } } else diff --git a/src/modules/powerrename/lib/Helpers.cpp b/src/modules/powerrename/lib/Helpers.cpp index f0476dd1dd..ecd823e4d7 100644 --- a/src/modules/powerrename/lib/Helpers.cpp +++ b/src/modules/powerrename/lib/Helpers.cpp @@ -420,7 +420,7 @@ BOOL GetEnumeratedFileName(__out_ecount(cchMax) PWSTR pszUniqueName, UINT cchMax if (!pszRest) { pszRest = PathFindExtension(pszTemplate); - cchStem = (int)(pszRest - pszTemplate); + cchStem = static_cast(pszRest - pszTemplate); hr = StringCchCopy(szFormat, ARRAYSIZE(szFormat), L" (%lu)"); } @@ -428,7 +428,7 @@ BOOL GetEnumeratedFileName(__out_ecount(cchMax) PWSTR pszUniqueName, UINT cchMax { pszRest++; - cchStem = (int)(pszRest - pszTemplate); + cchStem = static_cast(pszRest - pszTemplate); while (*pszRest && *pszRest >= L'0' && *pszRest <= L'9') { @@ -551,7 +551,7 @@ bool DataObjectContainsRenamableItem(_In_ IUnknown* dataSource) { bool hasRenamable = false; CComPtr spsia; - if (SUCCEEDED(GetShellItemArrayFromDataObject(dataSource, &spsia))) + if (dataSource && SUCCEEDED(GetShellItemArrayFromDataObject(dataSource, &spsia))) { CComPtr spesi; if (SUCCEEDED(spsia->EnumItems(&spesi))) @@ -581,7 +581,7 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p wc.lpfnWndProc = DefWindowProc; wc.cbWndExtra = sizeof(void*); wc.hInstance = hInst; - wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wc.hbrBackground = reinterpret_cast(COLOR_BTNFACE + 1); wc.lpszClassName = wndClassName; RegisterClass(&wc); @@ -590,10 +590,10 @@ HWND CreateMsgWindow(_In_ HINSTANCE hInst, _In_ WNDPROC pfnWndProc, _In_ void* p 0, wndClassName, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInst, nullptr); if (hwnd) { - SetWindowLongPtr(hwnd, 0, (LONG_PTR)p); + SetWindowLongPtr(hwnd, 0, reinterpret_cast(p)); if (pfnWndProc) { - SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast(pfnWndProc)); } } diff --git a/src/modules/powerrename/lib/MRUListHandler.cpp b/src/modules/powerrename/lib/MRUListHandler.cpp index 0465013a6e..71f796c507 100644 --- a/src/modules/powerrename/lib/MRUListHandler.cpp +++ b/src/modules/powerrename/lib/MRUListHandler.cpp @@ -123,12 +123,12 @@ void MRUListHandler::ParseJson() unsigned int oldSize{ size }; if (json::has(jsonObject, c_maxMRUSize, json::JsonValueType::Number)) { - oldSize = (unsigned int)jsonObject.GetNamedNumber(c_maxMRUSize); + oldSize = static_cast(jsonObject.GetNamedNumber(c_maxMRUSize)); } unsigned int oldPushIdx{ 0 }; if (json::has(jsonObject, c_insertionIdx, json::JsonValueType::Number)) { - oldPushIdx = (unsigned int)jsonObject.GetNamedNumber(c_insertionIdx); + oldPushIdx = static_cast(jsonObject.GetNamedNumber(c_insertionIdx)); if (oldPushIdx < 0 || oldPushIdx >= oldSize) { oldPushIdx = 0; @@ -156,7 +156,7 @@ void MRUListHandler::ParseJson() if (size > oldSize) { std::reverse(std::begin(temp), std::end(temp)); - pushIdx = (unsigned int)temp.size(); + pushIdx = static_cast(temp.size()); temp.resize(size); } else diff --git a/src/modules/powerrename/lib/PowerRenameManager.cpp b/src/modules/powerrename/lib/PowerRenameManager.cpp index f32ee46f7c..baa9ecf92f 100644 --- a/src/modules/powerrename/lib/PowerRenameManager.cpp +++ b/src/modules/powerrename/lib/PowerRenameManager.cpp @@ -526,7 +526,7 @@ LRESULT CALLBACK CPowerRenameManager::s_msgWndProc(_In_ HWND hwnd, _In_ UINT uMs { LRESULT lRes = 0; - CPowerRenameManager* pThis = (CPowerRenameManager*)GetWindowLongPtr(hwnd, 0); + CPowerRenameManager* pThis = reinterpret_cast(GetWindowLongPtr(hwnd, 0)); if (pThis != nullptr) { lRes = pThis->_WndProc(hwnd, uMsg, wParam, lParam); diff --git a/src/modules/powerrename/lib/Settings.cpp b/src/modules/powerrename/lib/Settings.cpp index f1470a1741..655b760e35 100644 --- a/src/modules/powerrename/lib/Settings.cpp +++ b/src/modules/powerrename/lib/Settings.cpp @@ -125,7 +125,7 @@ void CSettings::ParseJson() } if (json::has(jsonSettings, c_maxMRUSize, json::JsonValueType::Number)) { - settings.maxMRUSize = (unsigned int)jsonSettings.GetNamedNumber(c_maxMRUSize); + settings.maxMRUSize = static_cast(jsonSettings.GetNamedNumber(c_maxMRUSize)); } if (json::has(jsonSettings, c_searchText, json::JsonValueType::String)) { diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj index 954f426d4e..0e6af4e9d0 100644 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj +++ b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj @@ -53,8 +53,8 @@ - - + + diff --git a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs index 09a3c98c27..cc30703e2b 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs +++ b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.cs @@ -159,12 +159,10 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode using (var reader = new StreamReader(this.Stream)) { - using (Bitmap thumbnail = GetThumbnail(reader, cx)) + Bitmap thumbnail = GetThumbnail(reader, cx); + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) - { - return (Bitmap)thumbnail.Clone(); - } + return thumbnail; } } diff --git a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj index 11c1c3f3c9..9c3c2e3ff5 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj +++ b/src/modules/previewpane/GcodeThumbnailProvider/GcodeThumbnailProvider.csproj @@ -40,7 +40,7 @@ - + diff --git a/src/modules/previewpane/GcodeThumbnailProvider/Program.cs b/src/modules/previewpane/GcodeThumbnailProvider/Program.cs index 8426ca7755..e1b920feb6 100644 --- a/src/modules/previewpane/GcodeThumbnailProvider/Program.cs +++ b/src/modules/previewpane/GcodeThumbnailProvider/Program.cs @@ -26,8 +26,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode _thumbnailProvider = new GcodeThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".gcode", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null) + { + filePath = filePath.Replace(".gcode", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp index 7f16288d46..14b07431a2 100644 --- a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.cpp @@ -159,11 +159,19 @@ IFACEMETHODIMP GcodeThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS WaitForSingleObject(m_process, INFINITE); std::filesystem::remove(fileName); + std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; - - std::filesystem::remove(fileNameBmp); + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } } catch (std::exception& e) { diff --git a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h index 8662de66e6..91dabda4ad 100644 --- a/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h +++ b/src/modules/previewpane/GcodeThumbnailProviderCpp/GcodeThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); GcodeThumbnailProvider(); diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj index 0fc63177a4..b1b6da2ccd 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj @@ -1,4 +1,4 @@ - + enable true @@ -59,9 +59,9 @@ - - - + + + diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj index 9c1cd5d7e7..5668f2fa2e 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj @@ -49,9 +49,9 @@ - - - + + + @@ -66,6 +66,7 @@ + diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs index 6730cd700d..b534dc8931 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandlerControl.cs @@ -5,6 +5,8 @@ using System.Globalization; using System.Runtime.CompilerServices; using Common; +using ManagedCommon; +using Microsoft.PowerToys.PreviewHandler.Monaco.Formatters; using Microsoft.PowerToys.PreviewHandler.Monaco.Helpers; using Microsoft.PowerToys.PreviewHandler.Monaco.Properties; using Microsoft.Web.WebView2.Core; @@ -115,11 +117,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco if (fileSize < _settings.MaxFileSize) { - Task initializeIndexFileAndSelectedFileTask = new Task(() => { InitializeIndexFileAndSelectedFile(filePath); }); - initializeIndexFileAndSelectedFileTask.Start(); - try { + InitializeIndexFileAndSelectedFile(filePath); + Logger.LogInfo("Create WebView2 environment"); ConfiguredTaskAwaitable.ConfiguredTaskAwaiter webView2EnvironmentAwaiter = CoreWebView2Environment @@ -147,9 +148,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco { await _webView.EnsureCoreWebView2Async(_webView2Environment).ConfigureAwait(true); - // Wait until html is loaded - initializeIndexFileAndSelectedFileTask.Wait(); - _webView.CoreWebView2.SetVirtualHostNameToFolderMapping(FilePreviewCommon.MonacoHelper.VirtualHostName, Settings.AssemblyDirectory, CoreWebView2HostResourceAccessKind.Allow); Logger.LogInfo("Navigates to string of HTML file"); @@ -190,24 +188,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco downloadLink.Top = TextRenderer.MeasureText(Resources.WebView2_Not_Installed_Message, errorMessage.Font).Height + 10; downloadLink.Width = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Width + 10; downloadLink.Height = TextRenderer.MeasureText(Resources.Download_WebView2, errorMessage.Font).Height; + downloadLink.ForeColor = Settings.TextColor; Controls.Add(downloadLink); } }); } + catch (UnauthorizedAccessException e) + { + Logger.LogError(e.Message); + AddTextBoxControl(Resources.Access_Denied_Exception_Message); + } catch (Exception e) { - Controls.Remove(_loading); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); - Label text = new Label(); - text.Text = Resources.Exception_Occurred; - text.Text += e.Message; - text.Text += "\n" + e.Source; - text.Text += "\n" + e.StackTrace; - text.Width = 500; - text.Height = 10000; - Controls.Add(text); Logger.LogError(e.Message); + string errorMessage = Resources.Exception_Occurred; + errorMessage += e.Message; + errorMessage += "\n" + e.Source; + errorMessage += "\n" + e.StackTrace; + AddTextBoxControl(errorMessage); } this.Resize += FormResize; @@ -215,16 +213,7 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco else { Logger.LogInfo("File is too big to display. Showing error message"); - - Controls.Remove(_loading); - _loadingBar.Dispose(); - Controls.Remove(_loadingBar); - Controls.Remove(_loadingBackground); - Label errorMessage = new Label(); - errorMessage.Text = Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture); - errorMessage.Width = 500; - errorMessage.Height = 50; - Controls.Add(errorMessage); + AddTextBoxControl(Resources.Max_File_Size_Error.Replace("%1", (_settings.MaxFileSize / 1000).ToString(CultureInfo.CurrentCulture), StringComparison.InvariantCulture)); } } @@ -428,6 +417,10 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco /// Message to be displayed in textbox. private void AddTextBoxControl(string message) { + Controls.Remove(_loading); + Controls.Remove(_loadingBar); + Controls.Remove(_loadingBackground); + _textBox = new RichTextBox(); _textBox.Text = message; _textBox.BackColor = Color.LightYellow; diff --git a/src/modules/previewpane/MonacoPreviewHandler/Program.cs b/src/modules/previewpane/MonacoPreviewHandler/Program.cs index 8589b12c30..b6bcd511f8 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Program.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Program.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Windows.Threading; using Common.UI; using interop; +using ManagedCommon; namespace Microsoft.PowerToys.PreviewHandler.Monaco { @@ -21,6 +22,8 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco [STAThread] public static void Main(string[] args) { + Logger.InitializeLogger("\\FileExplorer_localLow\\Monaco\\logs", true); + ApplicationConfiguration.Initialize(); if (args != null) { diff --git a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs index cb056ae013..f318d5e89d 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.Designer.cs @@ -111,9 +111,20 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco.Properties { /// Looks up a localized string for an error when Gpo has the utility disabled. /// internal static string GpoDisabledErrorText { - get { + get + { return ResourceManager.GetString("GpoDisabledErrorText", resourceCulture); } } + + /// + /// Looks up a localized string for an error when access to file is denied. + /// + internal static string Access_Denied_Exception_Message { + get + { + return ResourceManager.GetString("Access_Denied_Exception_Message", resourceCulture); + } + } } } diff --git a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx index d0d97c0d42..25489d57a1 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx +++ b/src/modules/previewpane/MonacoPreviewHandler/Properties/Resources.resx @@ -140,4 +140,7 @@ Max file size: %1KB Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator. GPO stands for the Windows Group Policy Object feature. + + Access denied: You do not have permission to open this file. See the owner of the file or an administrator to obtain permission. + \ No newline at end of file diff --git a/src/modules/previewpane/MonacoPreviewHandler/Settings.cs b/src/modules/previewpane/MonacoPreviewHandler/Settings.cs index e03cbcf409..6bc7a11e76 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/Settings.cs +++ b/src/modules/previewpane/MonacoPreviewHandler/Settings.cs @@ -58,11 +58,24 @@ namespace Microsoft.PowerToys.PreviewHandler.Monaco } /// - /// Max file size for displaying (in bytes). + /// Gets Max file size for displaying (in bytes). /// - private readonly long _maxFileSize = 50000; - - public long MaxFileSize => _maxFileSize; + public double MaxFileSize + { + get + { + try + { + return moduleSettings.GetSettings(PowerPreviewSettings.ModuleName).Properties.MonacoPreviewMaxFileSize.Value * 1000; + } + catch (FileNotFoundException) + { + // Couldn't read the settings. + // Assume default of 50000. + return 50000; + } + } + } /// /// Gets the color of the window background. diff --git a/src/modules/previewpane/MonacoPreviewHandler/helpers/Logger.cs b/src/modules/previewpane/MonacoPreviewHandler/helpers/Logger.cs deleted file mode 100644 index 52d8e3f1e1..0000000000 --- a/src/modules/previewpane/MonacoPreviewHandler/helpers/Logger.cs +++ /dev/null @@ -1,82 +0,0 @@ -// 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. - -using System; -using System.Diagnostics; -using System.Globalization; -using System.IO.Abstractions; - -namespace Microsoft.PowerToys.PreviewHandler.Monaco.Helpers -{ - public static class Logger - { - private static readonly IFileSystem _fileSystem = new FileSystem(); - private static readonly string ApplicationLogPath = System.Environment.GetEnvironmentVariable("USERPROFILE") + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\logs\\FileExplorer_localLow\\Monaco"; - - static Logger() - { - if (!_fileSystem.Directory.Exists(ApplicationLogPath)) - { - _fileSystem.Directory.CreateDirectory(ApplicationLogPath); - } - - // Using InvariantCulture since this is used for a log file name - var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Monaco-log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); - - Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); - - Trace.AutoFlush = true; - } - - public static void LogError(string message) - { - Log(message, "ERROR"); - } - - public static void LogError(string message, Exception ex) - { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - "ERROR"); - } - - public static void LogWarning(string message) - { - Log(message, "WARNING"); - } - - public static void LogInfo(string message) - { - Log(message, "INFO"); - } - - private static void Log(string message, string type) - { - Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay); - Trace.Indent(); - Trace.WriteLine(GetCallerInfo()); - Trace.WriteLine(message); - Trace.Unindent(); - } - - public static void LogTrace() - { - Log(string.Empty, "Trace"); - } - - private static string GetCallerInfo() - { - StackTrace stackTrace = new StackTrace(); - - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return "[Method]: " + methodName?.Name + " [Class]: " + className; - } - } -} diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj index 9624934c81..8092dae64a 100644 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj +++ b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj @@ -54,8 +54,8 @@ - - + + diff --git a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs index 0852369f96..e82607b299 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs +++ b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.cs @@ -1,14 +1,8 @@ // 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. -using System; -using System.Drawing; -using System.IO; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using Common.ComInterlop; -using Common.Utilities; using Windows.Data.Pdf; +using Windows.Storage; using Windows.Storage.Streams; namespace Microsoft.PowerToys.ThumbnailHandler.Pdf @@ -21,7 +15,6 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf public PdfThumbnailProvider(string filePath) { FilePath = filePath; - Stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); } /// @@ -29,11 +22,6 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf /// public string FilePath { get; private set; } - /// - /// Gets the stream object to access file. - /// - public Stream Stream { get; private set; } - /// /// The maximum dimension (width or height) thumbnail we will generate. /// @@ -45,6 +33,16 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf /// Maximum thumbnail size, in pixels. /// Generated bitmap public Bitmap GetThumbnail(uint cx) + { + return DoGetThumbnail(cx).Result; + } + + /// + /// Generate thumbnail bitmap for provided Pdf file/stream. + /// + /// Maximum thumbnail size, in pixels. + /// Generated bitmap + private async Task DoGetThumbnail(uint cx) { if (cx == 0 || cx > MaxThumbnailSize) { @@ -57,26 +55,27 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf return null; } - using var memStream = new MemoryStream(); - - this.Stream.CopyTo(memStream); - memStream.Position = 0; - - // AsRandomAccessStream() extension method from System.Runtime.WindowsRuntime - var pdf = PdfDocument.LoadFromStreamAsync(memStream.AsRandomAccessStream()).GetAwaiter().GetResult(); - - if (pdf.PageCount > 0) + Bitmap thumbnail = null; + try { - using var page = pdf.GetPage(0); + var file = await StorageFile.GetFileFromPathAsync(FilePath); + var pdf = await PdfDocument.LoadFromFileAsync(file); - var image = PageToImage(page, cx); + if (pdf.PageCount > 0) + { + using var page = pdf.GetPage(0); - using Bitmap thumbnail = new Bitmap(image); + var image = PageToImage(page, cx); - return (Bitmap)thumbnail.Clone(); + thumbnail = new Bitmap(image); + } + } + catch (Exception) + { + // TODO: add logger } - return null; + return thumbnail; } /// diff --git a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj index af28de12f8..b3b5be4bc8 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj +++ b/src/modules/previewpane/PdfThumbnailProvider/PdfThumbnailProvider.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/modules/previewpane/PdfThumbnailProvider/Program.cs b/src/modules/previewpane/PdfThumbnailProvider/Program.cs index 59cf1acb99..5415a333ad 100644 --- a/src/modules/previewpane/PdfThumbnailProvider/Program.cs +++ b/src/modules/previewpane/PdfThumbnailProvider/Program.cs @@ -26,8 +26,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Pdf _thumbnailProvider = new PdfThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".pdf", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null) + { + filePath = filePath.Replace(".pdf", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp index 312512a454..75a0c7a038 100644 --- a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.cpp @@ -158,10 +158,18 @@ IFACEMETHODIMP PdfThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::filesystem::remove(fileName); std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } - std::filesystem::remove(fileNameBmp); } catch (std::exception& e) { diff --git a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h index 9ae9b43445..c63d6d37bd 100644 --- a/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h +++ b/src/modules/previewpane/PdfThumbnailProviderCpp/PdfThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); PdfThumbnailProvider(); diff --git a/src/modules/previewpane/StlThumbnailProvider/Program.cs b/src/modules/previewpane/StlThumbnailProvider/Program.cs index 77e904ba1e..ce22471f67 100644 --- a/src/modules/previewpane/StlThumbnailProvider/Program.cs +++ b/src/modules/previewpane/StlThumbnailProvider/Program.cs @@ -25,8 +25,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl _thumbnailProvider = new StlThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".stl", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null) + { + filePath = filePath.Replace(".stl", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs index c7d4ebdd14..aeecf4599a 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.cs @@ -130,19 +130,10 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Stl return null; } - using (var memStream = new MemoryStream()) + Bitmap thumbnail = GetThumbnail(this.Stream, cx); + if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) { - this.Stream.CopyTo(memStream); - - memStream.Position = 0; - - using (Bitmap thumbnail = GetThumbnail(memStream, cx)) - { - if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0) - { - return (Bitmap)thumbnail.Clone(); - } - } + return thumbnail; } return null; diff --git a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj index 016716c50a..ab2cb359a8 100644 --- a/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj +++ b/src/modules/previewpane/StlThumbnailProvider/StlThumbnailProvider.csproj @@ -40,9 +40,9 @@ - - - + + + diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp index eada9c4b06..9b1c8b8415 100644 --- a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.cpp @@ -158,10 +158,17 @@ IFACEMETHODIMP StlThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::filesystem::remove(fileName); std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; - - std::filesystem::remove(fileNameBmp); + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } } catch (std::exception& e) { diff --git a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h index ea97d69c82..fe7c4c5119 100644 --- a/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h +++ b/src/modules/previewpane/StlThumbnailProviderCpp/StlThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); StlThumbnailProvider(); diff --git a/src/modules/previewpane/SvgPreviewHandler/Settings.cs b/src/modules/previewpane/SvgPreviewHandler/Settings.cs new file mode 100644 index 0000000000..ee307e1003 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/Settings.cs @@ -0,0 +1,74 @@ +// 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. + +using Microsoft.PowerToys.Settings.UI.Library; + +namespace SvgPreviewHandler +{ + internal sealed class Settings + { + private static readonly SettingsUtils ModuleSettings = new SettingsUtils(); + + public int ColorMode + { + get + { + try + { + return ModuleSettings.GetSettings(PowerPreviewSettings.ModuleName).Properties.SvgBackgroundColorMode.Value; + } + catch (FileNotFoundException) + { + return PowerPreviewProperties.DefaultSvgBackgroundColorMode; + } + } + } + + public Color SolidColor + { + get + { + try + { + var colorString = ModuleSettings.GetSettings(PowerPreviewSettings.ModuleName).Properties.SvgBackgroundSolidColor.Value; + return ColorTranslator.FromHtml(colorString); + } + catch (FileNotFoundException) + { + return ColorTranslator.FromHtml(PowerPreviewProperties.DefaultSvgBackgroundSolidColor); + } + } + } + + public Color ThemeColor + { + get + { + if (Common.UI.ThemeManager.GetWindowsBaseColor().ToLowerInvariant() == "dark") + { + return ColorTranslator.FromHtml("#1e1e1e"); + } + else + { + return Color.White; + } + } + } + + public int CheckeredShade + { + get + { + try + { + return ModuleSettings.GetSettings(PowerPreviewSettings.ModuleName).Properties.SvgBackgroundCheckeredShade.Value; + } + catch (FileNotFoundException) + { + return PowerPreviewProperties.DefaultSvgBackgroundCheckeredShade; + } + } + } + } +} diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator.cs b/src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator.cs new file mode 100644 index 0000000000..bcb2fef0e6 --- /dev/null +++ b/src/modules/previewpane/SvgPreviewHandler/SvgHTMLPreviewGenerator.cs @@ -0,0 +1,64 @@ +// 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. + +using System.Globalization; +using Settings.UI.Library.Enumerations; + +namespace SvgPreviewHandler +{ + internal sealed class SvgHTMLPreviewGenerator + { + private const string CheckeredBackgroundShadeLight = """ + url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV/TiiIVBzOIOGSogmBBVMRRqlgEC6Wt0KqDyaVf0KQhSXFxFFwLDn4sVh1cnHV1cBUEwQ8QVxcnRRcp8X9JoUWMB8f9eHfvcfcOEBoVplmhCUDTbTMVj0nZ3KrU/YoQRIQxBlFmlpFIL2bgO77uEeDrXZRn+Z/7c/SpeYsBAYl4jhmmTbxBPLNpG5z3iUVWklXic+Jxky5I/Mh1xeM3zkWXBZ4pmpnUPLFILBU7WOlgVjI14mniiKrplC9kPVY5b3HWKjXWuid/YTivr6S5TnMYcSwhgSQkKKihjApsRGnVSbGQov2Yj3/I9SfJpZCrDEaOBVShQXb94H/wu1urMDXpJYVjQNeL43yMAN27QLPuON/HjtM8AYLPwJXe9lcbwOwn6fW2FjkC+reBi+u2puwBlzvA4JMhm7IrBWkKhQLwfkbflAMGboHeNa+31j5OH4AMdbV8AxwcAqNFyl73eXdPZ2//nmn19wOEPHKuuso0oQAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+cEFAwrEI+z8/sAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAL0lEQVQoz2O8e/cuAwzcv38fzlZUVMQqzsRAIqC9BhZi3I0sPhj9QIy7R+OB5hoACxUaWr81wGUAAAAASUVORK5CYII='); + """; + + private const string CheckeredBackgroundShadeMedium = """ + url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV/TiiIVBzOIOGSogmBBVMRRqlgEC6Wt0KqDyaVf0KQhSXFxFFwLDn4sVh1cnHV1cBUEwQ8QVxcnRRcp8X9JoUWMB8f9eHfvcfcOEBoVplmhCUDTbTMVj0nZ3KrU/YoQRIQxBlFmlpFIL2bgO77uEeDrXZRn+Z/7c/SpeYsBAYl4jhmmTbxBPLNpG5z3iUVWklXic+Jxky5I/Mh1xeM3zkWXBZ4pmpnUPLFILBU7WOlgVjI14mniiKrplC9kPVY5b3HWKjXWuid/YTivr6S5TnMYcSwhgSQkKKihjApsRGnVSbGQov2Yj3/I9SfJpZCrDEaOBVShQXb94H/wu1urMDXpJYVjQNeL43yMAN27QLPuON/HjtM8AYLPwJXe9lcbwOwn6fW2FjkC+reBi+u2puwBlzvA4JMhm7IrBWkKhQLwfkbflAMGboHeNa+31j5OH4AMdbV8AxwcAqNFyl73eXdPZ2//nmn19wOEPHKuuso0oQAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+cEFA0AJje78TwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAALklEQVQoz2O8e/cuAwwsX74czo6MjMQqzsRAIqC9BhZi3I0sPgj9wDgaD4PCDwBglRs7Q+IL6QAAAABJRU5ErkJggg=='); + """; + + private const string CheckeredBackgroundShadeDark = """ + url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV/TiiIVBzOIOGSogmBBVMRRqlgEC6Wt0KqDyaVf0KQhSXFxFFwLDn4sVh1cnHV1cBUEwQ8QVxcnRRcp8X9JoUWMB8f9eHfvcfcOEBoVplmhCUDTbTMVj0nZ3KrU/YoQRIQxBlFmlpFIL2bgO77uEeDrXZRn+Z/7c/SpeYsBAYl4jhmmTbxBPLNpG5z3iUVWklXic+Jxky5I/Mh1xeM3zkWXBZ4pmpnUPLFILBU7WOlgVjI14mniiKrplC9kPVY5b3HWKjXWuid/YTivr6S5TnMYcSwhgSQkKKihjApsRGnVSbGQov2Yj3/I9SfJpZCrDEaOBVShQXb94H/wu1urMDXpJYVjQNeL43yMAN27QLPuON/HjtM8AYLPwJXe9lcbwOwn6fW2FjkC+reBi+u2puwBlzvA4JMhm7IrBWkKhQLwfkbflAMGboHeNa+31j5OH4AMdbV8AxwcAqNFyl73eXdPZ2//nmn19wOEPHKuuso0oQAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+cEFA0CCa5crucAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAL0lEQVQoz2NsaWlhgIETJ07A2RYWFljFmRhIBLTXwEKMu5HFB6MfiHH3aDzQXAMACHkZChhWp4QAAAAASUVORK5CYII='); + """; + + private const string HtmlTemplateSolidColor = """ + + + {1} + + + """; + + private const string HtmlTemplateCheckered = """ + + + {1} + + + """; + + private readonly Settings settings = new(); + + public string GeneratePreview(string svgData) + { + var colorMode = (SvgPreviewColorMode)settings.ColorMode; + return colorMode switch + { + SvgPreviewColorMode.SolidColor => string.Format(CultureInfo.InvariantCulture, HtmlTemplateSolidColor, ColorTranslator.ToHtml(settings.SolidColor), svgData), + SvgPreviewColorMode.Checkered => string.Format(CultureInfo.InvariantCulture, HtmlTemplateCheckered, GetConfiguredCheckeredShadeImage(), svgData), + SvgPreviewColorMode.Default or _ => string.Format(CultureInfo.InvariantCulture, HtmlTemplateSolidColor, ColorTranslator.ToHtml(settings.ThemeColor), svgData), + }; + } + + private string GetConfiguredCheckeredShadeImage() + { + var checkeredShade = (SvgPreviewCheckeredShade)settings.CheckeredShade; + return checkeredShade switch + { + SvgPreviewCheckeredShade.Light=> CheckeredBackgroundShadeLight, + SvgPreviewCheckeredShade.Medium => CheckeredBackgroundShadeMedium, + SvgPreviewCheckeredShade.Dark or _ => CheckeredBackgroundShadeDark, + }; + } + } +} diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs index 907dde51aa..2cb2561729 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using Common; @@ -10,6 +11,7 @@ using Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events; using Microsoft.PowerToys.Telemetry; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; +using SvgPreviewHandler; namespace Microsoft.PowerToys.PreviewHandler.Svg { @@ -18,6 +20,11 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg /// public class SvgPreviewControl : FormHandlerControl { + /// + /// Generator for the actual preview file + /// + private readonly SvgHTMLPreviewGenerator _previewGenerator = new(); + /// /// WebView2 Control to display Svg. /// @@ -220,19 +227,21 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg _browser.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All); _browser.CoreWebView2.WebResourceRequested += CoreWebView2_BlockExternalResources; + string generatedPreview = _previewGenerator.GeneratePreview(svgData); + // WebView2.NavigateToString() limitation // See https://learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.navigatetostring?view=webview2-dotnet-1.0.864.35#remarks // While testing the limit, it turned out it is ~1.5MB, so to be on a safe side we go for 1.5m bytes - if (svgData.Length > 1_500_000) + if (generatedPreview.Length > 1_500_000) { string filename = _webView2UserDataFolder + "\\" + Guid.NewGuid().ToString() + ".html"; - File.WriteAllText(filename, svgData); + File.WriteAllText(filename, generatedPreview); _localFileURI = new Uri(filename); _browser.Source = _localFileURI; } else { - _browser.NavigateToString(svgData); + _browser.NavigateToString(generatedPreview); } Controls.Add(_browser); diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj index 0bf93c0376..c8b69ea1ad 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj @@ -59,12 +59,13 @@ + - - + + diff --git a/src/modules/previewpane/SvgThumbnailProvider/Program.cs b/src/modules/previewpane/SvgThumbnailProvider/Program.cs index f81a8f46ce..71172f0ac1 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/Program.cs +++ b/src/modules/previewpane/SvgThumbnailProvider/Program.cs @@ -26,8 +26,11 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Svg _thumbnailProvider = new SvgThumbnailProvider(filePath); Bitmap thumbnail = _thumbnailProvider.GetThumbnail(cx); - filePath = filePath.Replace(".svg", ".bmp"); - thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + if (thumbnail != null ) + { + filePath = filePath.Replace(".svg", ".bmp"); + thumbnail.Save(filePath, System.Drawing.Imaging.ImageFormat.Bmp); + } } else { diff --git a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj index d3f3a9cb98..8fa55a81d1 100644 --- a/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj @@ -42,8 +42,8 @@ - - + + diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp index 0f42b32f3b..ac749d0127 100644 --- a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp @@ -158,10 +158,18 @@ IFACEMETHODIMP SvgThumbnailProvider::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_A std::filesystem::remove(fileName); std::wstring fileNameBmp = filePath + guid + L".bmp"; - *phbmp = (HBITMAP)LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); - *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; - std::filesystem::remove(fileNameBmp); + if (std::filesystem::exists(fileNameBmp)) + { + *phbmp = static_cast(LoadImage(NULL, fileNameBmp.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); + *pdwAlpha = WTS_ALPHATYPE::WTSAT_ARGB; + std::filesystem::remove(fileNameBmp); + } + else + { + Logger::info(L"Bmp file not generated."); + return E_FAIL; + } } catch (std::exception& e) { diff --git a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h index 3c8a7f1b9c..b247dfa390 100644 --- a/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h +++ b/src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.h @@ -19,7 +19,7 @@ public: // IInitializeWithStream IFACEMETHODIMP Initialize(IStream* pstream, DWORD grfMode); - // IPreviewHandler + // IThumbnailProvider IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha); SvgThumbnailProvider(); diff --git a/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj b/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj index c6631ed82a..eaa97434a5 100644 --- a/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-GcodePreviewHandler/UnitTests-GcodePreviewHandler.csproj @@ -23,13 +23,13 @@ - - + + - - - + + + diff --git a/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj index cd699c14ed..e44437cbfa 100644 --- a/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-GcodeThumbnailProvider/UnitTests-GcodeThumbnailProvider.csproj @@ -24,11 +24,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj b/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj index 8b4580359c..1331df0748 100644 --- a/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-MarkdownPreviewHandler/UnitTests-MarkdownPreviewHandler.csproj @@ -20,12 +20,12 @@ - + - - - + + + diff --git a/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj b/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj index 0f85737aae..24bbef6441 100644 --- a/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-PdfPreviewHandler/UnitTests-PdfPreviewHandler.csproj @@ -25,13 +25,13 @@ - + - - - - + + + + diff --git a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs index a406c89ff5..635317e51e 100644 --- a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs +++ b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/PdfThumbnailProviderTests.cs @@ -22,7 +22,7 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailValidStreamPDF() { // Act - var filePath = "HelperFiles/sample.pdf"; + var filePath = System.IO.Path.GetFullPath("HelperFiles/sample.pdf"); PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); @@ -35,7 +35,7 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailInValidSizePDF() { // Act - var filePath = "HelperFiles/sample.pdf"; + var filePath = System.IO.Path.GetFullPath("HelperFiles/sample.pdf"); PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); @@ -48,7 +48,7 @@ namespace PdfThumbnailProviderUnitTests public void GetThumbnailToBigPDF() { // Act - var filePath = "HelperFiles/sample.pdf"; + var filePath = System.IO.Path.GetFullPath("HelperFiles/sample.pdf"); PdfThumbnailProvider provider = new PdfThumbnailProvider(filePath); diff --git a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj index ab3f789c23..d6d2e7c397 100644 --- a/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-PdfThumbnailProvider/UnitTests-PdfThumbnailProvider.csproj @@ -24,11 +24,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs index b1fd46d5bc..c4369063d4 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/FormHandlerControlTests.cs @@ -13,7 +13,7 @@ namespace PreviewHandlerCommonUnitTests [TestClass] public class FormHandlerControlTests { - private class TestFormControl : FormHandlerControl + private sealed class TestFormControl : FormHandlerControl { } diff --git a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj index 82b80eb59e..d2ee9ef320 100644 --- a/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj +++ b/src/modules/previewpane/UnitTests-PreviewHandlerCommon/UnitTests-PreviewHandlerCommon.csproj @@ -20,16 +20,16 @@ - - - + + + - + diff --git a/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj index c230f3a1b0..8b4b8200fa 100644 --- a/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-StlThumbnailProvider/UnitTests-StlThumbnailProvider.csproj @@ -24,11 +24,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj index 1214cb3c88..8d99a196d1 100644 --- a/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj +++ b/src/modules/previewpane/UnitTests-SvgPreviewHandler/UnitTests-SvgPreviewHandler.csproj @@ -25,13 +25,13 @@ - - - - + + + + - + diff --git a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj index f913ab7c08..2ddfad6f2a 100644 --- a/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj +++ b/src/modules/previewpane/UnitTests-SvgThumbnailProvider/UnitTests-SvgThumbnailProvider.csproj @@ -25,11 +25,11 @@ - - - - - + + + + + diff --git a/src/modules/previewpane/common/PreviewHandlerCommon.csproj b/src/modules/previewpane/common/PreviewHandlerCommon.csproj index 8a73c2a5b1..6c76eb14cd 100644 --- a/src/modules/previewpane/common/PreviewHandlerCommon.csproj +++ b/src/modules/previewpane/common/PreviewHandlerCommon.csproj @@ -26,8 +26,8 @@ - - + + \ No newline at end of file diff --git a/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs b/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs index fe71982fac..1c4f7b66de 100644 --- a/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs +++ b/src/modules/previewpane/common/Utilities/SvgPreviewHandlerHelper.cs @@ -17,7 +17,7 @@ namespace Common.Utilities { /// /// Dictionary of elements in lower case that are blocked from Svg for preview pane. - /// Reference for list of Svg Elements: https://developer.mozilla.org/en-US/docs/Web/SVG/Element. + /// Reference for list of Svg Elements: https://developer.mozilla.org/docs/Web/SVG/Element. /// private static Dictionary blockedElementsName = new Dictionary { diff --git a/src/modules/previewpane/common/cominterop/NativeMethods.cs b/src/modules/previewpane/common/cominterop/NativeMethods.cs index 3d7bebaef2..611136b1a3 100644 --- a/src/modules/previewpane/common/cominterop/NativeMethods.cs +++ b/src/modules/previewpane/common/cominterop/NativeMethods.cs @@ -10,7 +10,7 @@ namespace PreviewHandlerCommon.ComInterop /// /// Interop methods /// - internal class NativeMethods + internal sealed class NativeMethods { /// /// Changes the parent window of the specified child window. diff --git a/src/modules/previewpane/common/controls/FormHandlerControl.cs b/src/modules/previewpane/common/controls/FormHandlerControl.cs index 7549933382..ab5cfe6a91 100644 --- a/src/modules/previewpane/common/controls/FormHandlerControl.cs +++ b/src/modules/previewpane/common/controls/FormHandlerControl.cs @@ -73,7 +73,7 @@ namespace Common } /// - public void SetRect(Rectangle rect) + public void SetRect(Rectangle windowBounds) { this.UpdateWindowBounds(parentHwnd); } diff --git a/src/modules/registrypreview/RegistryPreviewExt/Constants.h b/src/modules/registrypreview/RegistryPreviewExt/Constants.h new file mode 100644 index 0000000000..5c81392657 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/Constants.h @@ -0,0 +1,7 @@ +#include + +namespace RegistryPreviewConstants +{ + // Name of the powertoy module. + inline const std::wstring ModuleKey = L"RegistryPreview"; +} \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc new file mode 100644 index 0000000000..e13a322c4f --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.rc @@ -0,0 +1,108 @@ +// Microsoft Visual C++ generated resource script. +// +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END + + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_REGISTRYPREVIEW_NAME "Registry Preview" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj new file mode 100644 index 0000000000..5f9b2fac10 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj @@ -0,0 +1,134 @@ + + + + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {697C6AF9-0A48-49A9-866C-67DA12384015} + Win32Proj + RegistryPreviewExt + 10.0.19041.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\modules\RegistryPreview\ + PowerToys.RegistryPreviewExt + + + + Level3 + true + _DEBUG;REGISTRYPREVIEWEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + Windows + true + false + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;REGISTRYPREVIEWEXT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + + + + + + + + Create + pch.h + + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters new file mode 100644 index 0000000000..bca37b946b --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/RegistryPreviewExt.vcxproj.filters @@ -0,0 +1,53 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + + + + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp b/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp new file mode 100644 index 0000000000..7dda85e43e --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp @@ -0,0 +1,40 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +// Log if the user has enabled or disabled the app +void Trace::EnableRegistryPreview(_In_ bool enabled) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "RegistryPreview_EnableRegistryPreview", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingBoolean(enabled, "Enabled")); +} + +// Log that the user tried to activate the app +void Trace::ActivateEditor() noexcept +{ + TraceLoggingWrite( + g_hProvider, + "RegistryPreview_Activate", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} diff --git a/src/modules/registrypreview/RegistryPreviewExt/Trace.h b/src/modules/registrypreview/RegistryPreviewExt/Trace.h new file mode 100644 index 0000000000..d2cda345d8 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/Trace.h @@ -0,0 +1,14 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + + // Log if the user has enabled or disabled the app + static void EnableRegistryPreview(const bool enabled) noexcept; + + // Log that the user tried to activate the app + static void ActivateEditor() noexcept; +}; diff --git a/src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp b/src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp new file mode 100644 index 0000000000..4ddac84299 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/dllmain.cpp @@ -0,0 +1,265 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" +#include +#include +#include "trace.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "resource.h" +#include "Constants.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +const static wchar_t* MODULE_NAME = L"RegistryPreview"; +const static wchar_t* MODULE_DESC = L"A quick little utility to visualize and edit complex Windows Registry files."; + +class RegistryPreviewModule : public PowertoyModuleIface +{ + +private: + bool m_enabled = false; + + //Hotkey m_hotkey; + HANDLE m_hProcess; + + HANDLE triggerEvent; + EventWaiter triggerEventWaiter; + + bool is_process_running() + { + return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; + } + + void launch_process() + { + if (m_enabled) + { + Logger::trace(L"Starting Registry Preview process"); + unsigned long powertoys_pid = GetCurrentProcessId(); + + std::wstring executable_args = L""; + executable_args.append(std::to_wstring(powertoys_pid)); + + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI }; + sei.lpFile = L"modules\\RegistryPreview\\PowerToys.RegistryPreview.exe"; + sei.nShow = SW_SHOWNORMAL; + sei.lpParameters = executable_args.data(); + if (ShellExecuteExW(&sei)) + { + Logger::trace("Successfully started the Registry Preview process"); + } + else + { + Logger::error(L"Registry Preview failed to start. {}", get_last_error_or_default(GetLastError())); + } + + m_hProcess = sei.hProcess; + } + } + + void terminate_process() + { + TerminateProcess(m_hProcess, 1); + } + +public: + RegistryPreviewModule() + { + LoggerHelpers::init_logger(GET_RESOURCE_STRING(IDS_REGISTRYPREVIEW_NAME), L"ModuleInterface", "RegistryPreview"); + Logger::info("Registry Preview object is constructing"); + + if (!m_enabled) + { + const std::wstring installationDir = get_module_folderpath(); + + auto regChanges = getRegistryPreviewChangeSet(installationDir, true); + + if (!regChanges.unApply()) + { + Logger::error(L"Unapplying registry changes failed"); + } + } + + triggerEvent = CreateEvent(nullptr, false, false, CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT); + triggerEventWaiter = EventWaiter(CommonSharedConstants::REGISTRY_PREVIEW_TRIGGER_EVENT, [this](int) { + on_hotkey(0); + }); + } + + ~RegistryPreviewModule() + { + if (m_enabled) + { + terminate_process(); + } + m_enabled = false; + } + + // Destroy the powertoy and free memory + virtual void destroy() override + { + delete this; + } + + // Return the localized display name of the powertoy + virtual const wchar_t* get_name() override + { + return MODULE_NAME; + } + + // Return the non localized key of the powertoy, this will be cached by the runner + virtual const wchar_t* get_key() override + { + return MODULE_NAME; + } + + // Return the configured status for the gpo policy for the module + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredRegistryPreviewEnabledValue(); + } + + // Return JSON with the configuration options. + virtual bool get_config(wchar_t* buffer, int* buffer_size) override + { + HINSTANCE hinstance = reinterpret_cast(&__ImageBase); + + // Create a Settings object. + PowerToysSettings::Settings settings(hinstance, get_name()); + settings.set_description(MODULE_DESC); + + return settings.serialize_to_buffer(buffer, buffer_size); + } + + // Pop open the app, if the OOBE page asks it to + virtual void call_custom_action(const wchar_t* action) override + { + try + { + PowerToysSettings::CustomActionObject action_object = + PowerToysSettings::CustomActionObject::from_json_string(action); + + if (action_object.get_name() == L"Launch") + { + launch_process(); + Trace::ActivateEditor(); + } + } + catch (std::exception&) + { + Logger::error(L"Failed to parse action. {}", action); + } + } + + // Called by the runner to pass the updated settings values as a serialized JSON. + virtual void set_config(const wchar_t* config) override + { + try + { + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); + + // If you don't need to do any custom processing of the settings, proceed + // to persists the values. + values.save_to_settings_file(); + } + catch (std::exception&) + { + // Improper JSON. + } + } + + // Enable the powertoy + virtual void enable() + { + const std::wstring installationDir = get_module_folderpath(); + + if (!getRegistryPreviewChangeSet(installationDir, true).apply()) + { + Logger::error(L"Applying registry changes failed"); + } + + // let the DLL enable the app + m_enabled = true; + Trace::EnableRegistryPreview(true); + }; + + virtual void disable() + { + if (m_enabled) + { + // let the DLL disable the app + terminate_process(); + + Trace::EnableRegistryPreview(false); + Logger::trace(L"Disabling Registry Preview..."); + + // Yeet the Registry setting so preview doesn't work anymore + const std::wstring installationDir = get_module_folderpath(); + + if (!getRegistryPreviewChangeSet(installationDir, true).unApply()) + { + Logger::error(L"Unapplying registry changes failed"); + } + } + + m_enabled = false; + } + + // Returns if the powertoys is enabled + virtual bool is_enabled() override + { + return m_enabled; + } + + // Respond to a "click" from the launcher + virtual bool on_hotkey(size_t /*hotkeyId*/) override + { + if (m_enabled) + { + Logger::trace(L"Registry Preview hotkey pressed"); + if (is_process_running()) + { + terminate_process(); + } + else + { + launch_process(); + } + + return true; + } + + return false; + } +}; + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new RegistryPreviewModule(); +} diff --git a/src/modules/registrypreview/RegistryPreviewExt/packages.config b/src/modules/registrypreview/RegistryPreviewExt/packages.config new file mode 100644 index 0000000000..c92dd4bf0c --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewExt/pch.cpp b/src/modules/registrypreview/RegistryPreviewExt/pch.cpp new file mode 100644 index 0000000000..1d9f38c57d --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/modules/registrypreview/RegistryPreviewExt/pch.h b/src/modules/registrypreview/RegistryPreviewExt/pch.h new file mode 100644 index 0000000000..be72eb015e --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/pch.h @@ -0,0 +1,16 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +//#include +#include +#include +#include + +#include + +#include +#include +//#include +#include +#include diff --git a/src/modules/registrypreview/RegistryPreviewExt/resource.h b/src/modules/registrypreview/RegistryPreviewExt/resource.h new file mode 100644 index 0000000000..a62c618221 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewExt/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Awake.rc +// +#define IDS_REGISTRYPREVIEW_NAME 101 + + +#define FILE_DESCRIPTION "PowerToys Registry Preview Module" +#define INTERNAL_NAME "PowerToys.RegistryPreview" +#define ORIGINAL_FILENAME "PowerToys.RegistryPreview.dll" + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/modules/registrypreview/RegistryPreviewUI/App.xaml b/src/modules/registrypreview/RegistryPreviewUI/App.xaml new file mode 100644 index 0000000000..ffd4f5f5ae --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/App.xaml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs new file mode 100644 index 0000000000..749a5bfcbe --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/App.xaml.cs @@ -0,0 +1,66 @@ +// 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. + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.UI.Xaml; +using Windows.ApplicationModel.Activation; +using LaunchActivatedEventArgs = Windows.ApplicationModel.Activation.LaunchActivatedEventArgs; + +namespace RegistryPreview +{ + /// + /// Provides application-specific behavior to supplement the default Application class. + /// + public partial class App : Application + { + /// + /// Initializes a new instance of the class. + /// + public App() + { + this.InitializeComponent(); + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + { + // Grab the command line parameters directly from the Environment since this is expected to be run + // via Context Menu of a REG file. + string[] cmdArgs = Environment.GetCommandLineArgs(); + if (cmdArgs == null) + { + // Covers the double click exe scenario and treated as no file loaded + AppFilename = string.Empty; + } + else if (cmdArgs.Length == 2) + { + // GetCommandLineArgs() send in the called EXE as 0 and the selected filename as 1 + AppFilename = cmdArgs[1]; + } + else + { + // Anything else should be treated as no file loaded + AppFilename = string.Empty; + } + + // Start the application + appWindow = new MainWindow(); + appWindow.Activate(); + } + + private Window appWindow; + +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable CA2211 // Non-constant fields should not be visible. TODO: consider making it a property + public static string AppFilename; +#pragma warning restore CA2211 // Non-constant fields should not be visible +#pragma warning restore SA1401 // Fields should be private + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/data32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/data32.png new file mode 100644 index 0000000000..de5afb66e9 Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/Assets/data32.png differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-folder32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-folder32.png new file mode 100644 index 0000000000..ccd53343a6 Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-folder32.png differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-value32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-value32.png new file mode 100644 index 0000000000..6bf283e6c9 Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/Assets/deleted-value32.png differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/error32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/error32.png new file mode 100644 index 0000000000..100bfed840 Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/Assets/error32.png differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/folder32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/folder32.png new file mode 100644 index 0000000000..ca0bd90c99 Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/Assets/folder32.png differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/Assets/string32.png b/src/modules/registrypreview/RegistryPreviewUI/Assets/string32.png new file mode 100644 index 0000000000..499f28daea Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/Assets/string32.png differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/FileName.cs b/src/modules/registrypreview/RegistryPreviewUI/FileName.cs new file mode 100644 index 0000000000..7464fff039 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/FileName.cs @@ -0,0 +1,39 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +namespace RegistryPreview +{ + // Workaround for File Pickers that don't work while running as admin, per: + // https://github.com/microsoft/WindowsAppSDK/issues/2504 + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct FileName + { + public int StructSize; + public IntPtr HwndOwner; + public IntPtr Instance; + public string Filter; + public string CustomFilter; + public int MaxCustFilter; + public int FilterIndex; + public string File; + public int MaxFile; + public string FileTitle; + public int MaxFileTitle; + public string InitialDir; + public string Title; + public int Flags; + public short FileOffset; + public short FileExtension; + public string DefExt; + public IntPtr CustData; + public IntPtr Hook; + public string TemplateName; + public IntPtr PtrReserved; + public int Reserved; + public int FlagsEx; + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs new file mode 100644 index 0000000000..9fbfbd1931 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Events.cs @@ -0,0 +1,370 @@ +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using CommunityToolkit.WinUI.UI.Controls; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Media; +using Windows.Data.Json; +using Windows.Foundation.Metadata; +using Windows.Storage; +using Windows.Storage.Pickers; +using WinRT.Interop; +using WinUIEx; + +namespace RegistryPreview +{ + public sealed partial class MainWindow : WindowEx + { + /// + /// Event handler to grab the main window's size and position before it closes + /// + private void AppWindow_Closing(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowClosingEventArgs args) + { + jsonSettings.SetNamedValue("appWindow.Position.X", JsonValue.CreateNumberValue(appWindow.Position.X)); + jsonSettings.SetNamedValue("appWindow.Position.Y", JsonValue.CreateNumberValue(appWindow.Position.Y)); + jsonSettings.SetNamedValue("appWindow.Size.Width", JsonValue.CreateNumberValue(appWindow.Size.Width)); + jsonSettings.SetNamedValue("appWindow.Size.Height", JsonValue.CreateNumberValue(appWindow.Size.Height)); + } + + /// + /// Event that is will prevent the app from closing if the "save file" flag is active + /// + public void Window_Closed(object sender, WindowEventArgs args) + { + // Only block closing if the REG file has been edited but not yet saved + if (saveButton.IsEnabled) + { + // if true, the app will not close + args.Handled = true; + + // ask the user if they want to save, discard or cancel the close; strings must be loaded here and passed to avoid timing issues + HandleDirtyClosing( + resourceLoader.GetString("YesNoCancelDialogTitle"), + resourceLoader.GetString("YesNoCancelDialogContent"), + resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"), + resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), + resourceLoader.GetString("YesNoCancelDialogCloseButtonText")); + } + + // Save app settings + SaveSettingsFile(settingsFolder, settingsFile); + } + + /// + /// Event that gets fired after the visual tree has been fully loaded; the app opens the reg file from here so it can show a message box successfully + /// + private void GridPreview_Loaded(object sender, RoutedEventArgs e) + { + // static flag to track whether the Visual Tree is ready - if the main Grid has been loaded, the tree is ready. + visualTreeReady = true; + + // Check to see if the REG file was opened and parsed successfully + if (OpenRegistryFile(App.AppFilename) == false) + { + if (File.Exists(App.AppFilename)) + { + // Allow Refresh and Edit to be enabled because a broken Reg file might be fixable + UpdateToolBarAndUI(false, true, true); + UpdateWindowTitle(resourceLoader.GetString("InvalidRegistryFileTitle")); + textBox.TextChanged += TextBox_TextChanged; + return; + } + else + { + UpdateToolBarAndUI(false, false, false); + UpdateWindowTitle(); + } + } + else + { + textBox.TextChanged += TextBox_TextChanged; + } + + textBox.Focus(FocusState.Programmatic); + } + + /// + /// Uses a picker to select a new file to open + /// + private async void OpenButton_Click(object sender, RoutedEventArgs e) + { + // Check to see if the current file has been saved + if (saveButton.IsEnabled) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = resourceLoader.GetString("YesNoCancelDialogTitle"), + Content = resourceLoader.GetString("YesNoCancelDialogContent"), + PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"), + SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), + CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"), + DefaultButton = ContentDialogButton.Primary, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + ContentDialogResult contentDialogResult = await contentDialog.ShowAsync(); + switch (contentDialogResult) + { + case ContentDialogResult.Primary: + // Save, then continue the file open + SaveFile(); + break; + case ContentDialogResult.Secondary: + // Don't save and continue the file open! + saveButton.IsEnabled = false; + break; + default: + // Don't open the new file! + return; + } + } + + // Pull in a new REG file - we have to use the direct Win32 method because FileOpenPicker crashes when it's + // called while running as admin + string filename = OpenFilePicker.ShowDialog( + resourceLoader.GetString("FilterRegistryName") + '\0' + "*.reg" + '\0' + resourceLoader.GetString("FilterAllFiles") + '\0' + "*.*" + '\0' + '\0', + resourceLoader.GetString("OpenDialogTitle")); + + if (filename == string.Empty || File.Exists(filename) == false) + { + return; + } + + StorageFile storageFile = await StorageFile.GetFileFromPathAsync(filename); + + if (storageFile != null) + { + // mute the TextChanged handler to make for clean UI + textBox.TextChanged -= TextBox_TextChanged; + + App.AppFilename = storageFile.Path; + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename)); + + // disable the Save button as it's a new file + saveButton.IsEnabled = false; + + // Restore the event handler as we're loaded + textBox.TextChanged += TextBox_TextChanged; + } + } + + /// + /// Saves the currently opened file in place + /// + private void SaveButton_Click(object sender, RoutedEventArgs e) + { + SaveFile(); + } + + /// + /// Uses a picker to save out a copy of the current reg file + /// + private void SaveAsButton_Click(object sender, RoutedEventArgs e) + { + // Save out a new REG file and then open it - we have to use the direct Win32 method because FileOpenPicker crashes when it's + // called while running as admin + string filename = SaveFilePicker.ShowDialog( + resourceLoader.GetString("SuggestFileName"), + resourceLoader.GetString("FilterRegistryName") + '\0' + "*.reg" + '\0' + resourceLoader.GetString("FilterAllFiles") + '\0' + "*.*" + '\0' + '\0', + resourceLoader.GetString("SaveDialogTitle")); + + if (filename == string.Empty) + { + return; + } + + App.AppFilename = filename; + SaveFile(); + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename)); + } + + /// + /// Reloads the current REG file from storage + /// + private void RefreshButton_Click(object sender, RoutedEventArgs e) + { + // mute the TextChanged handler to make for clean UI + textBox.TextChanged -= TextBox_TextChanged; + + // reload the current Registry file and update the toolbar accordingly. + UpdateToolBarAndUI(OpenRegistryFile(App.AppFilename), true, true); + + saveButton.IsEnabled = false; + + // restore the TextChanged handler + textBox.TextChanged += TextBox_TextChanged; + } + + /// + /// Opens the Registry Editor; UAC is handled by the request to open + /// + private void RegistryButton_Click(object sender, RoutedEventArgs e) + { + // pass in an empty string as we have no file to open + OpenRegistryEditor(string.Empty); + } + + /// + /// Opens the Registry Editor and tries to set "last used"; UAC is handled by the request to open + /// + private void RegistryJumpToKeyButton_Click(object sender, RoutedEventArgs e) + { + // Get the selected Key, if there is one + TreeViewNode currentNode = treeView.SelectedNode; + if (currentNode != null) + { + // since there is a valid node, get the FullPath of the key that was selected + string key = ((RegistryKey)currentNode.Content).FullPath; + + // it's impossible to directly open a key via command-line option, so we must override the last remember key + Microsoft.Win32.Registry.SetValue(@"HKEY_Current_User\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit", "LastKey", key); + } + + // pass in an empty string as we have no file to open + OpenRegistryEditor(string.Empty); + } + + /// + /// Merges the currently saved file into the Registry Editor; UAC is handled by the request to open + /// + private async void WriteButton_Click(object sender, RoutedEventArgs e) + { + // Check to see if the current file has been saved + if (saveButton.IsEnabled) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = resourceLoader.GetString("YesNoCancelDialogTitle"), + Content = resourceLoader.GetString("YesNoCancelDialogContent"), + PrimaryButtonText = resourceLoader.GetString("YesNoCancelDialogPrimaryButtonText"), + SecondaryButtonText = resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), + CloseButtonText = resourceLoader.GetString("YesNoCancelDialogCloseButtonText"), + DefaultButton = ContentDialogButton.Primary, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + ContentDialogResult contentDialogResult = await contentDialog.ShowAsync(); + switch (contentDialogResult) + { + case ContentDialogResult.Primary: + // Save, then continue the file open + SaveFile(); + break; + case ContentDialogResult.Secondary: + // Don't save and continue the file open! + saveButton.IsEnabled = false; + break; + default: + // Don't open the new file! + return; + } + } + + // pass in the filename so we can edit the current file + OpenRegistryEditor(App.AppFilename); + } + + /// + /// Opens the currently saved file in the PC's default REG file editor (often Notepad) + /// + private void EditButton_Click(object sender, RoutedEventArgs e) + { + // use the REG file's filename and verb so we can respect the selected editor + Process process = new Process(); + process.StartInfo.FileName = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", App.AppFilename); + process.StartInfo.Verb = "Edit"; + process.StartInfo.UseShellExecute = true; + + try + { + process.Start(); + } + catch + { + ShowMessageBox( + resourceLoader.GetString("ErrorDialogTitle"), + resourceLoader.GetString("FileEditorError"), + resourceLoader.GetString("OkButtonText")); + } + } + + /// + /// Trigger that fires when a node in treeView is clicked and which populates dataGrid + /// Can also be fired from elsewhere in the code + /// + private void TreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args) + { + TreeViewItemInvokedEventArgs localArgs = args as TreeViewItemInvokedEventArgs; + TreeViewNode treeViewNode = null; + + // if there are no args, the mouse didn't get clicked but we want to believe it did + if (args != null) + { + treeViewNode = args.InvokedItem as TreeViewNode; + } + else + { + treeViewNode = treeView.SelectedNode; + } + + // Update the toolbar button for the tree + registryJumpToKeyButton.IsEnabled = CheckTreeForValidKey(); + + // Grab the object that has Registry data in it from the currently selected treeView node + RegistryKey registryKey = (RegistryKey)treeViewNode.Content; + + // no matter what happens, clear the ListView of items on each click + ClearTable(); + + // if there's no ListView items stored for the selected node, dataGrid is clear so get out now + if (registryKey.Tag == null) + { + return; + } + + // if there WAS something in the Tag property, cast it to a list and Populate the ListView + ArrayList arrayList = (ArrayList)registryKey.Tag; + listRegistryValues = new List(); + + for (int i = 0; i < arrayList.Count; i++) + { + RegistryValue listViewItem = (RegistryValue)arrayList[i]; + listRegistryValues.Add(listViewItem); + } + + // create a new binding for dataGrid and reattach it, updating the rows + Binding listRegistryValuesBinding = new Binding { Source = listRegistryValues }; + dataGrid.SetBinding(DataGrid.ItemsSourceProperty, listRegistryValuesBinding); + } + + /// + /// When the text in textBox changes, reload treeView and possibly dataGrid and reset the save button + /// + private void TextBox_TextChanged(object sender, TextChangedEventArgs e) + { + RefreshRegistryFile(); + saveButton.IsEnabled = true; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs new file mode 100644 index 0000000000..3e89cf8069 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs @@ -0,0 +1,1059 @@ +// 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. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Windows.Foundation.Metadata; +using Windows.Storage; +using WinUIEx; + +namespace RegistryPreview +{ + public sealed partial class MainWindow : WindowEx + { + /// + /// Method that opens and processes the passed in file name; expected to be an absolute path and a first time open + /// + private bool OpenRegistryFile(string filename) + { + // clamp to prevent attempts to open a file larger than 10MB + try + { + long fileLength = new System.IO.FileInfo(filename).Length; + if (fileLength > 10485760) + { + ShowMessageBox(resourceLoader.GetString("LargeRegistryFileTitle"), App.AppFilename + resourceLoader.GetString("LargeRegistryFile"), resourceLoader.GetString("OkButtonText")); + ChangeCursor(gridPreview, false); + return false; + } + } + catch + { + // Do nothing here - a missing or invalid file will be caught below + } + + // Disable parts of the UI that can cause trouble when loading + ChangeCursor(gridPreview, true); + textBox.Text = string.Empty; + + // clear the treeView and dataGrid no matter what + treeView.RootNodes.Clear(); + ClearTable(); + + // update the current window's title with the current filename + UpdateWindowTitle(filename); + + // Load in the whole file in one call and plop it all into textBox + FileStream fileStream = null; + try + { + FileStreamOptions fileStreamOptions = new FileStreamOptions(); + fileStreamOptions.Access = FileAccess.Read; + fileStreamOptions.Share = FileShare.ReadWrite; + fileStreamOptions.Mode = FileMode.Open; + + fileStream = new FileStream(filename, fileStreamOptions); + StreamReader streamReader = new StreamReader(fileStream); + + string filenameText = streamReader.ReadToEnd(); + textBox.Text = filenameText; + streamReader.Close(); + } + catch + { + // restore TextChanged handler to make for clean UI + textBox.TextChanged += TextBox_TextChanged; + + // Reset the cursor but leave textBox disabled as no content got loaded + ChangeCursor(gridPreview, false); + return false; + } + finally + { + // clean up no matter what + if (fileStream != null) + { + fileStream.Dispose(); + } + } + + // now that the file is loaded and in textBox, parse the data + ParseRegistryFile(textBox.Text); + + // Getting here means that the entire REG file was parsed without incident + // so select the root of the tree and celebrate + if (treeView.RootNodes.Count > 0) + { + treeView.SelectedNode = treeView.RootNodes[0]; + treeView.Focus(FocusState.Programmatic); + } + + // reset the cursor + ChangeCursor(gridPreview, false); + return true; + } + + /// + /// Method that re-opens and processes the filename the app already knows about; expected to not be a first time open + /// + private void RefreshRegistryFile() + { + // Disable parts of the UI that can cause trouble when loading + ChangeCursor(gridPreview, true); + + // Get the current selected node so we can return focus to an existing node + TreeViewNode currentNode = treeView.SelectedNode; + + // clear the treeView and dataGrid no matter what + treeView.RootNodes.Clear(); + ClearTable(); + + // the existing text is still in textBox so parse the data again + ParseRegistryFile(textBox.Text); + + // check to see if there was a key in treeView before the refresh happened + if (currentNode != null) + { + // since there is a valid node, get the FullPath of the key that was selected + string selectedFullPath = ((RegistryKey)currentNode.Content).FullPath; + + // check to see if we still have the key in the new Dictionary of keys + if (mapRegistryKeys.ContainsKey(selectedFullPath)) + { + // we found it! select it in the tree and pretend it was selected + TreeViewNode treeViewNode; + mapRegistryKeys.TryGetValue(selectedFullPath, out treeViewNode); + treeView.SelectedNode = treeViewNode; + TreeView_ItemInvoked(treeView, null); + } + else + { + // we failed to find an existing node; it could have been deleted in the edit + if (treeView.RootNodes.Count > 0) + { + treeView.SelectedNode = treeView.RootNodes[0]; + } + } + } + else + { + // no node was previously selected so check for a RootNode and select it + if (treeView.RootNodes.Count > 0) + { + treeView.SelectedNode = treeView.RootNodes[0]; + } + } + + // Update the toolbar button for the trees + registryJumpToKeyButton.IsEnabled = CheckTreeForValidKey(); + + // enable the UI + ChangeCursor(gridPreview, false); + } + + /// + /// Parses the text that is passed in, which should be the same text that's in textBox + /// + private bool ParseRegistryFile(string filenameText) + { + // if this is a not-first open, clear out the Dictionary of nodes + if (mapRegistryKeys != null) + { + mapRegistryKeys.Clear(); + mapRegistryKeys = null; + } + + // set up a new dictionary + mapRegistryKeys = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + // As we'll be processing the text one line at a time, this string will be the current line + string registryLine; + + // Brute force editing: for textBox to show Cr-Lf corrected, we need to strip out the \n's + filenameText = filenameText.Replace("\r\n", "\r"); + + // split apart all of the text in textBox, where one element in the array represents one line + string[] registryLines = filenameText.Split("\r"); + if (registryLines.Length <= 1) + { + // after the split, we have no lines so get out + ChangeCursor(gridPreview, false); + return false; + } + + // REG files have to start with one of two headers and it's case insensitive + registryLine = registryLines[0]; + registryLine = registryLine.ToLowerInvariant(); + + // make sure that this is a valid REG file, based on the first line of the file + switch (registryLine) + { + case REGISTRYHEADER4: + case REGISTRYHEADER5: + break; + default: + ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + ChangeCursor(gridPreview, false); + return false; + } + + // these are used for populating the tree as we read in one line at a time + TreeViewNode treeViewNode = null; + RegistryValue registryValue = null; + + // start with the first element of the array + int index = 1; + registryLine = registryLines[index]; + + while (index < registryLines.Length) + { + // special case for when the registryLine begins with a @ - make some tweaks and + // let the regular processing handle the rest. + if (registryLine.StartsWith("@=-", StringComparison.InvariantCulture)) + { + // REG file has a callout to delete the @ Value which won't work *but* the Registry Editor will + // clear the value of the @ Value instead, so it's still a valid line. + registryLine = registryLine.Replace("@=-", "\"(Default)\"=\"\""); + } + else if (registryLine.StartsWith("@=", StringComparison.InvariantCulture)) + { + // This is the a Value called "(Default)" so we tweak the line for the UX + registryLine = registryLine.Replace("@=", "\"(Default)\"="); + } + + // continue until we have nothing left to read + // switch logic, based off what the current line we're reading is + if (registryLine.StartsWith("[-", StringComparison.InvariantCulture)) + { + // remove the - as we won't need it but it will get special treatment in the UI + registryLine = registryLine.Remove(1, 1); + + string imageName = DELETEDKEYIMAGE; + CheckKeyLineForBrackets(ref registryLine, ref imageName); + + // this is a key, so remove the first [ and last ] + registryLine = StripFirstAndLast(registryLine); + + // do not track the result of this node, since it should have no children + AddTextToTree(registryLine, imageName); + } + else if (registryLine.StartsWith("[", StringComparison.InvariantCulture)) + { + string imageName = KEYIMAGE; + CheckKeyLineForBrackets(ref registryLine, ref imageName); + + // this is a key, so remove the first [ and last ] + registryLine = StripFirstAndLast(registryLine); + + treeViewNode = AddTextToTree(registryLine, imageName); + } + else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture) && registryLine.EndsWith("=-", StringComparison.InvariantCulture)) + { + // this line deletes this value so it gets special treatment for the UI + registryLine = registryLine.Replace("=-", string.Empty); + + // remove the "'s without removing all of them + registryLine = StripFirstAndLast(registryLine); + + // Create a new listview item that will be used to display the delete value and store it + registryValue = new RegistryValue(registryLine, string.Empty, string.Empty); + SetValueToolTip(registryValue); + + // store the ListViewItem, if we have a valid Key to attach to + if (treeViewNode != null) + { + StoreTheListValue((RegistryKey)treeViewNode.Content, registryValue); + } + } + else if (registryLine.StartsWith("\"", StringComparison.InvariantCulture)) + { + // this is a named value + + // split up the name from the value by looking for the first found = + int equal = registryLine.IndexOf('='); + if ((equal < 0) || (equal > registryLine.Length - 1)) + { + // something is very wrong + Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "SOMETHING WENT WRONG: {0}", registryLine)); + break; + } + + // set the name and the value + string name = registryLine.Substring(0, equal); + name = StripFirstAndLast(name); + + // Clean out any escaped characters in the value, only for the preview + name = StripEscapedCharacters(name); + + string value = registryLine.Substring(equal + 1); + + // Create a new listview item that will be used to display the value + registryValue = new RegistryValue(name, "REG_SZ", string.Empty); + + // if the first character is a " then this is a string value, so find the last most " which will avoid comments + if (value.StartsWith("\"", StringComparison.InvariantCulture)) + { + int last = value.LastIndexOf("\"", StringComparison.InvariantCulture); + if (last >= 0) + { + value = value.Substring(0, last + 1); + } + } + + if (value.StartsWith("\"", StringComparison.InvariantCulture) && value.EndsWith("\"", StringComparison.InvariantCulture)) + { + value = StripFirstAndLast(value); + } + else + { + // this is an invalid value as there are no "s in the right side of the = + registryValue.Type = "ERROR"; + } + + if (value.StartsWith("dword:", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_DWORD"; + value = value.Replace("dword:", string.Empty); + } + else if (value.StartsWith("hex(b):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_QWORD"; + value = value.Replace("hex(b):", string.Empty); + } + else if (value.StartsWith("hex:", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_BINARY"; + value = value.Replace("hex:", string.Empty); + } + else if (value.StartsWith("hex(2):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_EXPAND_SZ"; + value = value.Replace("hex(2):", string.Empty); + } + else if (value.StartsWith("hex(7):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_MULTI_SZ"; + value = value.Replace("hex(7):", string.Empty); + } + else if (value.StartsWith("hex(0):", StringComparison.InvariantCultureIgnoreCase)) + { + registryValue.Type = "REG_NONE"; + value = value.Replace("hex(0):", string.Empty); + } + + // special casing for various key types + switch (registryValue.Type) + { + case "REG_SZ": + case "ERROR": + + // no special handling for these two + break; + default: + // check to see if a continuation marker is the first character + if (value == @"\") + { + // pad the value, so the parsing below is triggered + value = @",\"; + } + + value = ScanAndRemoveComments(value); + + break; + } + + // Parse for the case where a \ is added immediately after hex is declared + switch (registryValue.Type) + { + case "REG_QWORD": + case "REG_BINARY": + case "REG_EXPAND_SZ": + case "REG_MULTI_SZ": + if (value == @"\") + { + // pad the value, so the parsing below is triggered + value = @",\"; + } + + break; + } + + // If the end of a decimal line ends in a \ then you have to keep + // reading the block as a single value! + while (value.EndsWith(@",\", StringComparison.InvariantCulture)) + { + value = value.TrimEnd('\\'); + + // checking for a "blank" hex value so we can skip t + if (value == @",") + { + value = string.Empty; + } + + index++; + if (index >= registryLines.Length) + { + ChangeCursor(gridPreview, false); + return false; + } + + registryLine = registryLines[index]; + registryLine = ScanAndRemoveComments(registryLine); + registryLine = registryLine.TrimStart(); + value += registryLine; + } + + // Clean out any escaped characters in the value, only for the preview + value = StripEscapedCharacters(value); + + // update the ListViewItem with the loaded value, based off REG value type + switch (registryValue.Type) + { + case "ERROR": + // do nothing + break; + case "REG_BINARY": + case "REG_NONE": + if (value.Length <= 0) + { + value = resourceLoader.GetString("ZeroLength"); + } + + registryValue.Value = value; + + break; + default: + registryValue.Value = value; + break; + } + + // update the ToolTip + SetValueToolTip(registryValue); + + // store the ListViewItem, if we have a valid Key to attach to + if (treeViewNode != null) + { + StoreTheListValue((RegistryKey)treeViewNode.Content, registryValue); + } + } + + // if we get here, it's not a Key (starts with [) or Value (starts with ") so it's likely waste (comments that start with ; fall out here) + + // read the next line from the REG file + index++; + + // if we've gone too far, escape the proc! + if (index >= registryLines.Length) + { + // check to see if anything got parsed! + if (treeView.RootNodes.Count <= 0) + { + AddTextToTree(resourceLoader.GetString("NoNodesFoundInFile"), ERRORIMAGE); + + // ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + } + + ChangeCursor(gridPreview, false); + return false; + } + + // carry on with the next line + registryLine = registryLines[index]; + } + + // last check, to see if anything got parsed! + if (treeView.RootNodes.Count <= 0) + { + ShowMessageBox(APPNAME, App.AppFilename + resourceLoader.GetString("InvalidRegistryFile"), resourceLoader.GetString("OkButtonText")); + ChangeCursor(gridPreview, false); + return false; + } + + return true; + } + + /// + /// We're going to store this ListViewItem in an ArrayList which will then + /// be attached to the most recently returned TreeNode that came back from + /// AddTextToTree. If there's already a list there, we will use that list and + /// add our new node to it. + /// + private void StoreTheListValue(RegistryKey registryKey, RegistryValue registryValue) + { + ArrayList arrayList = null; + if (registryKey.Tag == null) + { + arrayList = new ArrayList(); + } + else + { + arrayList = (ArrayList)registryKey.Tag; + } + + arrayList.Add(registryValue); + + // shove the updated array into the Tag property + registryKey.Tag = arrayList; + } + + /// + /// Adds the REG file that's being currently being viewed to the app's title bar + /// + private void UpdateWindowTitle(string filename) + { + string[] file = filename.Split('\\'); + if (file.Length > 0) + { + appWindow.Title = file[file.Length - 1] + " - " + APPNAME; + } + else + { + appWindow.Title = filename + " - " + APPNAME; + } + } + + /// + /// No REG file was opened, so leave the app's title bar alone + /// + private void UpdateWindowTitle() + { + appWindow.Title = APPNAME; + } + + /// + /// Helper method that assumes everything is enabled/disabled together + /// + private void UpdateToolBarAndUI(bool enable) + { + UpdateToolBarAndUI(enable, enable, enable); + } + + /// + /// Enable command bar buttons and textBox. + /// Note that writeButton and textBox all update with the same value on purpose + /// + private void UpdateToolBarAndUI(bool enableWrite, bool enableRefresh, bool enableEdit) + { + refreshButton.IsEnabled = enableRefresh; + editButton.IsEnabled = enableEdit; + writeButton.IsEnabled = enableWrite; + + // Now check the tree and see if anything is in there + registryJumpToKeyButton.IsEnabled = CheckTreeForValidKey(); + } + + /// + /// Helper method that creates a new TreeView node, attaches it to a parent if any, and then passes the new node back to the caller + /// mapRegistryKeys is a collection of all of the [] lines in the file + /// keys comes from the REG file and represents a bunch of nodes + /// + private TreeViewNode AddTextToTree(string keys, string image) + { + string[] individualKeys = keys.Split('\\'); + + string fullPath = keys; + TreeViewNode returnNewNode = null, newNode = null, previousNode = null; + + // Walk the list of keys backwards + for (int i = individualKeys.Length - 1; i >= 0; i--) + { + // when a Key is marked for deletion, make sure it only sets the icon for the bottom most leaf + if (image == DELETEDKEYIMAGE) + { + if (i < individualKeys.Length - 1) + { + image = KEYIMAGE; + } + else + { + // special casing for Registry roots + switch (individualKeys[i]) + { + case "HKEY_CLASSES_ROOT": + case "HKEY_CURRENT_USER": + case "HKEY_LOCAL_MACHINE": + case "HKEY_USERS": + case "HKEY_CURRENT_CONFIG": + image = KEYIMAGE; + break; + } + } + } + + // First check the dictionary, and return the current node if it already exists + if (mapRegistryKeys.ContainsKey(fullPath)) + { + // was a new node created? + if (returnNewNode == null) + { + // if no new nodes have been created, send out the node we should have already + mapRegistryKeys.TryGetValue(fullPath, out returnNewNode); + } + else + { + // as a new node was created, hook it up to this found parent + mapRegistryKeys.TryGetValue(fullPath, out newNode); + newNode.Children.Add(previousNode); + } + + // return the new node no matter what + return returnNewNode; + } + + // Since the path is not in the tree, create a new node and add it to the dictionary + RegistryKey registryKey = new RegistryKey(individualKeys[i], fullPath, image, GetFolderToolTip(image)); + + newNode = new TreeViewNode() { Content = registryKey, IsExpanded = true }; + mapRegistryKeys.Add(fullPath, newNode); + + // if this is the first new node we're creating, we need to return it to the caller + if (previousNode == null) + { + // capture the first node so it can be returned + returnNewNode = newNode; + } + else + { + // The newly created node is a parent to the previously created node, as add it here. + newNode.Children.Add(previousNode); + } + + // before moving onto the next node, tag the previous node and update the path + previousNode = newNode; + fullPath = fullPath.Replace(string.Format(CultureInfo.InvariantCulture, @"\{0}", individualKeys[i]), string.Empty); + + // One last check: if we get here, the parent of this node is not yet in the tree, so we need to add it as a RootNode + if (i == 0) + { + treeView.RootNodes.Add(newNode); + treeView.UpdateLayout(); + } + } + + return returnNewNode; + } + + /// + /// Wrapper method that shows a simple one-button message box, parented by the main application window + /// + private async void ShowMessageBox(string title, string content, string closeButtonText) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = title, + Content = content, + CloseButtonText = closeButtonText, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + await contentDialog.ShowAsync(); + } + + /// + /// Wrapper method that shows a Save/Don't Save/Cancel message box, parented by the main application window and shown when closing the app + /// + private async void HandleDirtyClosing(string title, string content, string primaryButtonText, string secondaryButtonText, string closeButtonText) + { + ContentDialog contentDialog = new ContentDialog() + { + Title = title, + Content = content, + PrimaryButtonText = primaryButtonText, + SecondaryButtonText = secondaryButtonText, + CloseButtonText = closeButtonText, + DefaultButton = ContentDialogButton.Primary, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + ContentDialogResult contentDialogResult = await contentDialog.ShowAsync(); + + switch (contentDialogResult) + { + case ContentDialogResult.Primary: + // Save, then close + SaveFile(); + break; + case ContentDialogResult.Secondary: + // Don't save, and then close! + saveButton.IsEnabled = false; + break; + default: + // Cancel closing! + return; + } + + // if we got here, we should try to close again + App.Current.Exit(); + } + + /// + /// Method will open the Registry Editor or merge the current REG file into the Registry via the Editor + /// Process will prompt for elevation if it needs it. + /// + private void OpenRegistryEditor(string fileMerge) + { + Process process = new Process(); + process.StartInfo.FileName = "regedit.exe"; + process.StartInfo.UseShellExecute = true; + if (File.Exists(fileMerge)) + { + // If Merge was called, pass in the filename as a param to the Editor + process.StartInfo.Arguments = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", fileMerge); + } + + try + { + process.Start(); + } + catch + { + ShowMessageBox( + resourceLoader.GetString("UACDialogTitle"), + resourceLoader.GetString("UACDialogError"), + resourceLoader.GetString("OkButtonText")); + } + } + + /// + /// Utility method that clears out the GridView as there's no other way to do it. + /// + private void ClearTable() + { + if (listRegistryValues != null) + { + listRegistryValues.Clear(); + } + + dataGrid.ItemsSource = null; + } + + /// + /// Change the current app cursor at the grid level to be a wait cursor. Sort of works, sort of doesn't, but it's a nice attempt. + /// + public void ChangeCursor(UIElement uiElement, bool wait) + { + // You can only change the Cursor if the visual tree is loaded + if (!visualTreeReady) + { + return; + } + + InputCursor cursor = InputSystemCursor.Create(wait ? InputSystemCursorShape.Wait : InputSystemCursorShape.Arrow); + System.Type type = typeof(UIElement); + type.InvokeMember("ProtectedCursor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, uiElement, new object[] { cursor }, CultureInfo.InvariantCulture); + } + + /// + /// Wrapper method that saves the current file in place, using the current text in textBox. + /// + private void SaveFile() + { + ChangeCursor(gridPreview, true); + + // set up the FileStream for all writing + FileStream fileStream = null; + + try + { + // attempt to open the existing file for writing + FileStreamOptions fileStreamOptions = new FileStreamOptions(); + fileStreamOptions.Access = FileAccess.Write; + fileStreamOptions.Share = FileShare.Write; + fileStreamOptions.Mode = FileMode.OpenOrCreate; + + fileStream = new FileStream(App.AppFilename, fileStreamOptions); + StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.Unicode); + + // if we get here, the file is open and writable so dump the whole contents of textBox + string filenameText = textBox.Text; + streamWriter.Write(filenameText); + streamWriter.Flush(); + streamWriter.Close(); + + // only change when the save is successful + saveButton.IsEnabled = false; + } + catch (UnauthorizedAccessException ex) + { + // this exception is thrown if the file is there but marked as read only + ShowMessageBox( + resourceLoader.GetString("ErrorDialogTitle"), + ex.Message, + resourceLoader.GetString("OkButtonText")); + } + catch + { + // this catch handles all other exceptions thrown when trying to write the file out + ShowMessageBox( + resourceLoader.GetString("ErrorDialogTitle"), + resourceLoader.GetString("FileSaveError"), + resourceLoader.GetString("OkButtonText")); + } + finally + { + // clean up no matter what + if (fileStream != null) + { + fileStream.Dispose(); + } + } + + // restore the cursor + ChangeCursor(gridPreview, false); + } + + private void OpenSettingsFile(string path, string filename) + { + string fileContents = string.Empty; + string storageFile = Path.Combine(path, filename); + if (File.Exists(storageFile)) + { + try + { + TextReader reader = new StreamReader(storageFile); + fileContents = reader.ReadToEnd(); + reader.Close(); + } + catch + { + // set up default JSON blob + fileContents = "{ }"; + } + } + + try + { + jsonSettings = Windows.Data.Json.JsonObject.Parse(fileContents); + } + catch + { + // set up default JSON blob + fileContents = "{ }"; + jsonSettings = Windows.Data.Json.JsonObject.Parse(fileContents); + } + } + + /// + /// Save the settings JSON blob out to a local file + /// + private async void SaveSettingsFile(string path, string filename) + { + StorageFolder storageFolder = null; + StorageFile storageFile = null; + string fileContents = string.Empty; + + try + { + storageFolder = await StorageFolder.GetFolderFromPathAsync(path); + } + catch (FileNotFoundException ex) + { + Debug.WriteLine(ex.Message); + Directory.CreateDirectory(path); + storageFolder = await StorageFolder.GetFolderFromPathAsync(path); + } + + try + { + storageFile = await storageFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists); + } + catch (FileNotFoundException ex) + { + Debug.WriteLine(ex.Message); + storageFile = await storageFolder.CreateFileAsync(filename); + } + + try + { + fileContents = jsonSettings.Stringify(); + await Windows.Storage.FileIO.WriteTextAsync(storageFile, fileContents); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + } + + /// + /// Rip the first and last character off a string, + /// checking that the string is at least 2 characters long to avoid errors + /// + private string StripFirstAndLast(string line) + { + if (line.Length > 1) + { + line = line.Remove(line.Length - 1, 1); + line = line.Remove(0, 1); + } + + return line; + } + + /// + /// Replace any escaped characters in the REG file with their counterparts, for the UX + /// + private string StripEscapedCharacters(string value) + { + value = value.Replace("\\\\", "\\"); // Replace \\ with \ in the UI + value = value.Replace("\\\"", "\""); // Replace \" with " in the UI + return value; + } + + /// + /// Loads and returns a string for a given Key's image in the tree, based off the current set image + /// + private string GetFolderToolTip(string key) + { + string value = string.Empty; + switch (key) + { + case DELETEDKEYIMAGE: + value = resourceLoader.GetString("ToolTipDeletedKey"); + break; + case KEYIMAGE: + value = resourceLoader.GetString("ToolTipAddedKey"); + break; + case ERRORIMAGE: + value = resourceLoader.GetString("ToolTipErrorKey"); + break; + } + + return value; + } + + /// + /// Loads a string for a given Value's image in the grid, based off the current type and updates the RegistryValue that's passed in + /// + private void SetValueToolTip(RegistryValue registryValue) + { + string value = string.Empty; + switch (registryValue.Type) + { + case "REG_SZ": + case "REG_EXAND_SZ": + case "REG_MULTI_SZ": + value = resourceLoader.GetString("ToolTipStringValue"); + break; + case "ERROR": + value = resourceLoader.GetString("ToolTipErrorValue"); + break; + case "": + value = resourceLoader.GetString("ToolTipDeletedValue"); + break; + default: + value = resourceLoader.GetString("ToolTipBinaryValue"); + break; + } + + registryValue.ToolTipText = value; + } + + /// + /// Checks a Key line for the closing bracket and treat it as an error if it cannot be found + /// + private void CheckKeyLineForBrackets(ref string registryLine, ref string imageName) + { + // following the current behavior of the registry editor, find the last ] and treat everything else as ignorable + int lastBracket = registryLine.LastIndexOf(']'); + if (lastBracket == -1) + { + // since we don't have a last bracket yet, add an extra space and continue processing + registryLine += " "; + imageName = ERRORIMAGE; + } + else + { + // having found the last ] and there is text after it, drop the rest of the string on the floor + if (lastBracket < registryLine.Length - 1) + { + registryLine = registryLine.Substring(0, lastBracket + 1); + } + + if (CheckForKnownGoodBranches(registryLine) == false) + { + imageName = ERRORIMAGE; + } + } + } + + /// + /// Takes a binary registry value, sees if it has a ; and dumps the rest of the line - this does not work for REG_SZ values + /// + private string ScanAndRemoveComments(string value) + { + // scan for comments and remove them + int indexOf = value.IndexOf(";", StringComparison.InvariantCulture); + if (indexOf > -1) + { + // presume that there is nothing following the start of the comment + value = value.Remove(indexOf, value.Length - indexOf); + } + + return value; + } + + /// + /// Make sure the root of a full path start with one of the five "hard coded" roots. Throw an error for the branch if it doesn't. + /// + private bool CheckForKnownGoodBranches(string key) + { + if ((key.StartsWith("[HKEY_CLASSES_ROOT]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_CURRENT_USER]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_USERS]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_LOCAL_MACHINE]", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith("[HKEY_CURRENT_CONFIG]", StringComparison.InvariantCultureIgnoreCase) == false) + && + (key.StartsWith(@"[HKEY_CLASSES_ROOT\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_CURRENT_USER\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_USERS\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_LOCAL_MACHINE\", StringComparison.InvariantCultureIgnoreCase) == false && + key.StartsWith(@"[HKEY_CURRENT_CONFIG\", StringComparison.InvariantCultureIgnoreCase) == false)) + { + return false; + } + + return true; + } + + /// + /// Turns the Open Key button in the command bar on/off, depending on if a key is selected + /// + private bool CheckTreeForValidKey() + { + if (treeView == null) + { + return false; + } + + // See if a key is available + TreeViewNode treeViewNode = treeView.SelectedNode; + if (treeViewNode != null && ((RegistryKey)treeViewNode.Content).Image != ERRORIMAGE) + { + return true; + } + + return false; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml new file mode 100644 index 0000000000..b59d229bbb --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs new file mode 100644 index 0000000000..d2f977aa16 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.xaml.cs @@ -0,0 +1,97 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.UI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Windows.ApplicationModel.Resources; +using Windows.Data.Json; +using Windows.Graphics; +using WinUIEx; + +namespace RegistryPreview +{ + public sealed partial class MainWindow : WindowEx + { + // Const values + private const string REGISTRYHEADER4 = "regedit4"; + private const string REGISTRYHEADER5 = "windows registry editor version 5.00"; + private const string APPNAME = "Registry Preview"; + private const string KEYIMAGE = "ms-appx:///Assets/folder32.png"; + private const string DELETEDKEYIMAGE = "ms-appx:///Assets/deleted-folder32.png"; + private const string ERRORIMAGE = "ms-appx:///Assets/error32.png"; + + // private members + private Microsoft.UI.Windowing.AppWindow appWindow; + private ResourceLoader resourceLoader; + private bool visualTreeReady; + private Dictionary mapRegistryKeys; + private List listRegistryValues; + private JsonObject jsonSettings; + private string settingsFolder = string.Empty; + private string settingsFile = string.Empty; + + internal MainWindow() + { + this.InitializeComponent(); + + // Initialize the string table + resourceLoader = ResourceLoader.GetForViewIndependentUse(); + + // Open settings file; this moved to after the window tweak because it gives the window time to start up + settingsFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerToys\" + APPNAME; + settingsFile = APPNAME + "_settings.json"; + OpenSettingsFile(settingsFolder, settingsFile); + + // Update the Win32 looking window with the correct icon (and grab the appWindow handle for later) + IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle); + appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId); + appWindow.SetIcon("app.ico"); + appWindow.Closing += AppWindow_Closing; + + // if have settings, update the location of the window + if (jsonSettings != null) + { + // resize the window + if (jsonSettings.ContainsKey("appWindow.Size.Width") && jsonSettings.ContainsKey("appWindow.Size.Height")) + { + SizeInt32 size; + size.Width = (int)jsonSettings.GetNamedNumber("appWindow.Size.Width"); + size.Height = (int)jsonSettings.GetNamedNumber("appWindow.Size.Height"); + + // check to make sure the size values are reasonable before attempting to restore the last saved size + if (size.Width >= 320 && size.Height >= 240) + { + appWindow.Resize(size); + } + } + + // reposition the window + if (jsonSettings.ContainsKey("appWindow.Position.X") && jsonSettings.ContainsKey("appWindow.Position.Y")) + { + PointInt32 point; + point.X = (int)jsonSettings.GetNamedNumber("appWindow.Position.X"); + point.Y = (int)jsonSettings.GetNamedNumber("appWindow.Position.Y"); + + // check to make sure the move values are reasonable before attempting to restore the last saved location + if (point.X >= 0 && point.Y >= 0) + { + appWindow.Move(point); + } + } + } + + // Update Toolbar + if ((App.AppFilename == null) || (File.Exists(App.AppFilename) != true)) + { + UpdateToolBarAndUI(false); + UpdateWindowTitle(resourceLoader.GetString("FileNotFound")); + } + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs b/src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs new file mode 100644 index 0000000000..c6dc243aeb --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/OpenFileName.cs @@ -0,0 +1,36 @@ +// 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. + +using System.Runtime.InteropServices; + +namespace RegistryPreview +{ + // Workaround for File Pickers that don't work while running as admin, per: + // https://github.com/microsoft/WindowsAppSDK/issues/2504 + public static partial class OpenFilePicker + { + [DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool GetOpenFileName(ref FileName openFileName); + + public static string ShowDialog(string filter, string dialogTitle) + { + FileName openFileName = default(FileName); + openFileName.StructSize = Marshal.SizeOf(openFileName); + + openFileName.Filter = filter; + openFileName.File = new string(new char[256]); + openFileName.MaxFile = openFileName.File.Length; + openFileName.FileTitle = new string(new char[64]); + openFileName.MaxFileTitle = openFileName.FileTitle.Length; + openFileName.Title = dialogTitle; + + if (GetOpenFileName(ref openFileName)) + { + return openFileName.File; + } + + return string.Empty; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs new file mode 100644 index 0000000000..343966fa3d --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryKey.xaml.cs @@ -0,0 +1,32 @@ +// 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. + +namespace RegistryPreview +{ + /// + /// Class representing an each item in the tree view, each one a Registry Key; + /// FullPath is so we can re-select the node after a live update + /// Tag is an Array of ListViewItems that stores all the children for the current object + /// + public class RegistryKey + { + public string Name { get; set; } + + public string FullPath { get; set; } + + public string Image { get; set; } + + public string ToolTipText { get; set; } + + public object Tag { get; set; } + + public RegistryKey(string name, string fullPath, string image, string toolTipText) + { + this.Name = name; + this.FullPath = fullPath; + this.Image = image; + this.ToolTipText = toolTipText; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj new file mode 100644 index 0000000000..9e68b9e7b4 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryPreviewUI.csproj @@ -0,0 +1,69 @@ + + + + WinExe + net7.0-windows10.0.19041.0 + 10.0.19041.0 + x86;x64;arm64 + $(SolutionDir)$(Platform)\$(Configuration)\modules\RegistryPreview + true + False + app.ico + app.manifest + true + false + false + true + None + true + 10.0.19041.0 + true + $(AssemblyName) + PowerToys.RegistryPreview + PowerToys.RegistryPreview + PowerToys RegistryPreview + RegistryPreview + true + true + + + + + win10-x64 + + + win10-arm64 + + + + + + + + + https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json + + + + + + + + + + + + + + + + + + Never + + + Never + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs new file mode 100644 index 0000000000..ce372281b3 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs @@ -0,0 +1,57 @@ +// 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. + +using System; + +namespace RegistryPreview +{ + /// + /// Class representing an each item in the list view, each one a Registry Value. + /// + public class RegistryValue + { + // Static members + private static Uri uriStringValue = new Uri("ms-appx:///Assets/string32.png"); + private static Uri uriBinaryValue = new Uri("ms-appx:///Assets/data32.png"); + private static Uri uriDeleteValue = new Uri("ms-appx:///Assets/deleted-value32.png"); + private static Uri uriErrorValue = new Uri("ms-appx:///Assets/error32.png"); + + public string Name { get; set; } + + public string Type { get; set; } + + public string Value { get; set; } + + public string ToolTipText { get; set; } + + public Uri ImageUri + { + // Based off the Type of the item, pass back the correct image Uri used by the Binding of the DataGrid + get + { + switch (Type) + { + case "REG_SZ": + case "REG_EXAND_SZ": + case "REG_MULTI_SZ": + return uriStringValue; + case "ERROR": + return uriErrorValue; + case "": + return uriDeleteValue; + } + + return uriBinaryValue; + } + } + + public RegistryValue(string name, string type, string value) + { + this.Name = name; + this.Type = type; + this.Value = value; + this.ToolTipText = string.Empty; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs b/src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs new file mode 100644 index 0000000000..0e0f2d58a7 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/SaveFileName.cs @@ -0,0 +1,38 @@ +// 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. + +using System.Runtime.InteropServices; + +namespace RegistryPreview +{ + // Workaround for File Pickers that don't work while running as admin, per: + // https://github.com/microsoft/WindowsAppSDK/issues/2504 + public static partial class SaveFilePicker + { + [DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool GetSaveFileName(ref FileName saveFileName); + + public static string ShowDialog(string suggestedFilename, string filter, string dialogTitle) + { + FileName saveFileName = default(FileName); + saveFileName.StructSize = Marshal.SizeOf(saveFileName); + + saveFileName.Filter = filter; + saveFileName.File = new string(new char[256]); + saveFileName.MaxFile = saveFileName.File.Length; + saveFileName.File = string.Concat(suggestedFilename, saveFileName.File); + saveFileName.FileTitle = new string(new char[64]); + saveFileName.MaxFileTitle = saveFileName.FileTitle.Length; + saveFileName.Title = dialogTitle; + saveFileName.DefExt = "reg"; + + if (GetSaveFileName(ref saveFileName)) + { + return saveFileName.File; + } + + return string.Empty; + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw new file mode 100644 index 0000000000..d0b843f39f --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Edit file... + + + Error + + + The REG file editor could not be opened. + + + File was not found! + + + The REG file cannot be written to. + + + All files (*.*) + + + Registry files (*.reg) + + + doesn't appear to be a valid registry file! + + + File was not a Registry file! + + + is larger than 10MB which is too large for this application. + + + File is too large! + + + Name + + + No valid Keys were found + + + OK + + + Open file... + + + Open Registry file... + + + Reload from file + + + Open Registry Editor... + + + Open Key... + + + Save file as... + + + Save file + + + Save + + + New Registry File + + + Registry file text will appear here... + + + Key will be added, if needed + + + Binary value will be updated + + + Key will be deleted + + + Value will be deleted + + + Key couldn't be parsed + + + Value has a syntax error + + + String value will be updated + + + Type + + + You must click Yes on the previous popup if you want to run the Registry application. + + + User Account Control + + + Value + + + Write to Registry... + + + Cancel + + + Changes were made to the text file. Do you want to save your changes? + + + Save + + + Don't save + + + Registry Preview + + + (zero-length binary value) + + \ No newline at end of file diff --git a/src/modules/registrypreview/RegistryPreviewUI/app.ico b/src/modules/registrypreview/RegistryPreviewUI/app.ico new file mode 100644 index 0000000000..0b83ebb9fd Binary files /dev/null and b/src/modules/registrypreview/RegistryPreviewUI/app.ico differ diff --git a/src/modules/registrypreview/RegistryPreviewUI/app.manifest b/src/modules/registrypreview/RegistryPreviewUI/app.manifest new file mode 100644 index 0000000000..0871bb63b2 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUI/app.manifest @@ -0,0 +1,21 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + + + + + + + diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Dark.png b/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Dark.png index c22c77705b..08446a2078 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Dark.png and b/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Dark.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Light.png b/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Light.png index aab6946637..285ea495b8 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Light.png and b/src/modules/videoconference/VideoConferenceModule/Icons/Off-NotInUse Light.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Dark.png b/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Dark.png index eb0895c6db..32d7559e73 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Dark.png and b/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Dark.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Light.png b/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Light.png index 160023f4c4..7f0477ff89 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Light.png and b/src/modules/videoconference/VideoConferenceModule/Icons/Off-Off Light.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Dark.png b/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Dark.png index 0055fee566..ac33d985c8 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Dark.png and b/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Dark.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Light.png b/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Light.png index db03c24ffe..a43b3cf364 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Light.png and b/src/modules/videoconference/VideoConferenceModule/Icons/Off-On Light.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Dark.png b/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Dark.png index 67eabf8f1a..ff34ff3eb5 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Dark.png and b/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Dark.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Light.png b/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Light.png index cac03b85cd..b4e5edaea5 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Light.png and b/src/modules/videoconference/VideoConferenceModule/Icons/On-NotInUse Light.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Dark.png b/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Dark.png index 3c072fc2cf..8492439759 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Dark.png and b/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Dark.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Light.png b/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Light.png index e54271890d..b011e944bb 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Light.png and b/src/modules/videoconference/VideoConferenceModule/Icons/On-Off Light.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/On-On Dark.png b/src/modules/videoconference/VideoConferenceModule/Icons/On-On Dark.png index 325e5cffd2..6f1955d2d2 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/On-On Dark.png and b/src/modules/videoconference/VideoConferenceModule/Icons/On-On Dark.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Icons/On-On Light.png b/src/modules/videoconference/VideoConferenceModule/Icons/On-On Light.png index fce4e9baa3..b9fb04c6a6 100644 Binary files a/src/modules/videoconference/VideoConferenceModule/Icons/On-On Light.png and b/src/modules/videoconference/VideoConferenceModule/Icons/On-On Light.png differ diff --git a/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp b/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp index 2cd5f0fb64..58eaaa4b65 100644 --- a/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp +++ b/src/modules/videoconference/VideoConferenceModule/Toolbar.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "Logging.h" #include "VideoConferenceModule.h" @@ -14,6 +15,7 @@ const int REFRESH_RATE = 100; const int OVERLAY_SHOW_TIME = 500; const int BORDER_OFFSET = 12; const int TOP_RIGHT_BORDER_OFFSET = 40; +std::wstring cached_position = L""; Toolbar::Toolbar() { @@ -43,6 +45,44 @@ void Toolbar::scheduleGeneralSettingsUpdate() generalSettingsUpdateScheduled = true; } +inline POINT calculateToolbarPositioning(Box const& screenSize, std::wstring& position, const int desiredWidth, const int desiredHeight) +{ + POINT p; + p.x = p.y = 0; + + if (position == L"Top left corner") + { + p.x = screenSize.left() + BORDER_OFFSET; + p.y = screenSize.top() + BORDER_OFFSET; + } + else if (position == L"Top center") + { + p.x = screenSize.middle().x - desiredWidth / 2; + p.y = screenSize.top() + BORDER_OFFSET; + } + else if (position == L"Bottom left corner") + { + p.x = screenSize.left() + BORDER_OFFSET; + p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET; + } + else if (position == L"Bottom center") + { + p.x = screenSize.middle().x - desiredWidth / 2; + p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET; + } + else if (position == L"Bottom right corner") + { + p.x = screenSize.right() - desiredWidth - BORDER_OFFSET; + p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET; + } + else //"Top right corner" or non-present + { + p.x = screenSize.right() - desiredWidth - BORDER_OFFSET; + p.y = screenSize.top() + TOP_RIGHT_BORDER_OFFSET; + } + return p; +} + LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) @@ -53,8 +93,10 @@ LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARA { int x = GET_X_LPARAM(lparam); int y = GET_Y_LPARAM(lparam); + UINT dpi = DPIAware::DEFAULT_DPI; + DPIAware::GetScreenDPIForWindow(hwnd, dpi); - if (x < 322 / 2) + if (x < 322 * static_cast(dpi) / static_cast(DPIAware::DEFAULT_DPI) / 2) { VideoConferenceModule::reverseMicrophoneMute(); } @@ -65,11 +107,41 @@ LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARA return DefWindowProcW(hwnd, msg, wparam, lparam); } + case WM_DPICHANGED: + { + UINT dpi = LOWORD(dpi); + RECT* prcNewWindow = reinterpret_cast(lparam); + + POINT suggestedPosition; + suggestedPosition.x = prcNewWindow->left; + suggestedPosition.y = prcNewWindow->top; + + int desiredWidth = prcNewWindow->right - prcNewWindow->left; + int desiredHeight = prcNewWindow->bottom - prcNewWindow->top; + + HMONITOR hMonitor = MonitorFromPoint(suggestedPosition, MONITOR_DEFAULTTONEAREST); + + MonitorInfo info{ hMonitor }; + + suggestedPosition = calculateToolbarPositioning(info.GetScreenSize(false), cached_position, desiredWidth, desiredHeight); + + SetWindowPos(hwnd, + NULL, + suggestedPosition.x, + suggestedPosition.y, + desiredWidth, + desiredHeight, + SWP_NOZORDER | SWP_NOACTIVATE); + return DefWindowProcW(hwnd, msg, wparam, lparam); + } case WM_CREATE: case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; + UINT dpi = DPIAware::DEFAULT_DPI; + + DPIAware::GetScreenDPIForWindow(hwnd, dpi); hdc = BeginPaint(hwnd, &ps); @@ -119,7 +191,8 @@ LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARA toolbarImage = themeImages->camOnMicOn; } } - graphic.DrawImage(toolbarImage, 0, 0, toolbarImage->GetWidth(), toolbarImage->GetHeight()); + // Images are scaled by 4 to support higher dpi values. + graphic.DrawImage(toolbarImage, 0, 0, toolbarImage->GetWidth() / 4 * dpi / DPIAware::DEFAULT_DPI, toolbarImage->GetHeight() / 4 * dpi / DPIAware::DEFAULT_DPI); EndPaint(hwnd, &ps); break; @@ -194,14 +267,16 @@ LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARA void Toolbar::show(std::wstring position, std::wstring monitorString) { + cached_position = position; for (auto& hwnd : hwnds) { PostMessageW(hwnd, WM_CLOSE, 0, 0); } hwnds.clear(); - int overlayWidth = darkImages.camOffMicOff->GetWidth(); - int overlayHeight = darkImages.camOffMicOff->GetHeight(); + // Images are scaled by 4 to support higher dpi values. + int overlayWidth = darkImages.camOffMicOff->GetWidth() / 4; + int overlayHeight = darkImages.camOffMicOff->GetHeight() / 4; // Register the window class LPCWSTR CLASS_NAME = L"MuteNotificationWindowClass"; @@ -209,7 +284,7 @@ void Toolbar::show(std::wstring position, std::wstring monitorString) wc.hInstance = GetModuleHandleW(nullptr); wc.lpszClassName = CLASS_NAME; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.hbrBackground = reinterpret_cast(COLOR_WINDOW); wc.lpfnWndProc = WindowProcessMessages; RegisterClassW(&wc); @@ -231,39 +306,13 @@ void Toolbar::show(std::wstring position, std::wstring monitorString) for (auto& monitorInfo : monitorInfos) { const auto screenSize = monitorInfo.GetScreenSize(false); - int positionX = 0; - int positionY = 0; + UINT dpi = DPIAware::DEFAULT_DPI; + DPIAware::GetScreenDPIForMonitor(monitorInfo.GetHandle(), dpi); - if (position == L"Top left corner") - { - positionX = screenSize.left() + BORDER_OFFSET; - positionY = screenSize.top() + BORDER_OFFSET; - } - else if (position == L"Top center") - { - positionX = screenSize.middle().x - overlayWidth / 2; - positionY = screenSize.top() + BORDER_OFFSET; - } - else if (position == L"Bottom left corner") - { - positionX = screenSize.left() + BORDER_OFFSET; - positionY = screenSize.bottom() - overlayHeight - BORDER_OFFSET; - } - else if (position == L"Bottom center") - { - positionX = screenSize.middle().x - overlayWidth / 2; - positionY = screenSize.bottom() - overlayHeight - BORDER_OFFSET; - } - else if (position == L"Bottom right corner") - { - positionX = screenSize.right() - overlayWidth - BORDER_OFFSET; - positionY = screenSize.bottom() - overlayHeight - BORDER_OFFSET; - } - else //"Top right corner" or non-present - { - positionX = screenSize.right() - overlayWidth - BORDER_OFFSET; - positionY = screenSize.top() + TOP_RIGHT_BORDER_OFFSET; - } + int scaledOverlayWidth = overlayWidth * dpi / DPIAware::DEFAULT_DPI; + int scaledOverlayHeight = overlayHeight * dpi / DPIAware::DEFAULT_DPI; + + POINT p = calculateToolbarPositioning(screenSize, position, scaledOverlayWidth, scaledOverlayHeight); HWND hwnd; hwnd = CreateWindowExW( @@ -271,10 +320,10 @@ void Toolbar::show(std::wstring position, std::wstring monitorString) CLASS_NAME, CLASS_NAME, WS_POPUP, - positionX, - positionY, - overlayWidth, - overlayHeight, + static_cast(p.x), + static_cast(p.y), + scaledOverlayWidth, + scaledOverlayHeight, nullptr, nullptr, GetModuleHandleW(nullptr), @@ -282,7 +331,7 @@ void Toolbar::show(std::wstring position, std::wstring monitorString) auto transparentColorKey = RGB(0, 0, 255); HBRUSH brush = CreateSolidBrush(transparentColorKey); - SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush); + SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, reinterpret_cast(brush)); SetLayeredWindowAttributes(hwnd, transparentColorKey, 0, LWA_COLORKEY); diff --git a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp index 6d0400936a..d4b370fe78 100644 --- a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp +++ b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp @@ -26,6 +26,7 @@ VideoConferenceModule* instance = nullptr; VideoConferenceSettings VideoConferenceModule::settings; Toolbar VideoConferenceModule::toolbar; +bool VideoConferenceModule::pushToTalkPressed = false; HHOOK VideoConferenceModule::hook_handle; @@ -123,10 +124,10 @@ LRESULT CALLBACK VideoConferenceModule::LowLevelKeyboardProc(int nCode, WPARAM w { if (nCode == HC_ACTION) { + KBDLLHOOKSTRUCT* kbd = reinterpret_cast(lParam); switch (wParam) { case WM_KEYDOWN: - KBDLLHOOKSTRUCT* kbd = reinterpret_cast(lParam); if (isHotkeyPressed(kbd->vkCode, settings.cameraAndMicrophoneMuteHotkey)) { @@ -163,11 +164,31 @@ LRESULT CALLBACK VideoConferenceModule::LowLevelKeyboardProc(int nCode, WPARAM w reverseMicrophoneMute(); return 1; } + else if (isHotkeyPressed(kbd->vkCode, settings.microphonePushToTalkHotkey)) + { + if (!pushToTalkPressed) + { + if (settings.pushToReverseEnabled || getMicrophoneMuteState()) + { + reverseMicrophoneMute(); + } + pushToTalkPressed = true; + } + return 1; + } else if (isHotkeyPressed(kbd->vkCode, settings.cameraMuteHotkey)) { reverseVirtualCameraMuteState(); return 1; } + break; + case WM_KEYUP: + if (pushToTalkPressed && (kbd->vkCode == settings.microphonePushToTalkHotkey.get_code())) + { + reverseMicrophoneMute(); + pushToTalkPressed = false; + return 1; + } } } @@ -228,6 +249,14 @@ void VideoConferenceModule::onModuleSettingsChanged() { settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val); } + if (const auto val = values.get_json(L"push_to_talk_microphone_hotkey")) + { + settings.microphonePushToTalkHotkey = PowerToysSettings::HotkeyObject::from_json(*val); + } + if (const auto val = values.get_bool_value(L"push_to_reverse_enabled")) + { + settings.pushToReverseEnabled = *val; + } if (const auto val = values.get_json(L"mute_camera_hotkey")) { settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val); @@ -386,6 +415,14 @@ void VideoConferenceModule::init_settings() { settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val); } + if (const auto val = powerToysSettings.get_json(L"push_to_talk_microphone_hotkey")) + { + settings.microphonePushToTalkHotkey = PowerToysSettings::HotkeyObject::from_json(*val); + } + if (const auto val = powerToysSettings.get_bool_value(L"push_to_reverse_enabled")) + { + settings.pushToReverseEnabled = *val; + } if (const auto val = powerToysSettings.get_json(L"mute_camera_hotkey")) { settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val); diff --git a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h index d156c6493e..fdd838e60d 100644 --- a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h +++ b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h @@ -20,6 +20,7 @@ struct VideoConferenceSettings { PowerToysSettings::HotkeyObject cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 81); PowerToysSettings::HotkeyObject microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 65); + PowerToysSettings::HotkeyObject microphonePushToTalkHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 73); PowerToysSettings::HotkeyObject cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 79); std::wstring toolbarPositionString; @@ -28,6 +29,8 @@ struct VideoConferenceSettings std::wstring selectedCamera; std::wstring imageOverlayPath; std::wstring selectedMicrophone; + + bool pushToReverseEnabled = false; }; class VideoConferenceModule : public PowertoyModuleIface @@ -93,4 +96,5 @@ private: static VideoConferenceSettings settings; static Toolbar toolbar; + static bool pushToTalkPressed; }; diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp index 31af9c3289..4887801154 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/ImageLoading.cpp @@ -111,7 +111,7 @@ bool ReencodeJPGImage(BYTE* imageBuf, const DWORD imageSize, DWORD& reencodedSiz auto jpgStreamMemory = static_cast(GlobalLock(streamMemoryHandle)); std::copy(jpgStreamMemory, jpgStreamMemory + jpgStreamSize, imageBuf); auto unlockJpgStreamMemory = wil::scope_exit([jpgStreamMemory] { GlobalUnlock(jpgStreamMemory); }); - reencodedSize = (DWORD)jpgStreamSize; + reencodedSize = static_cast(jpgStreamSize); return true; } diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp index 62d2b7933f..7b97891c5b 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureDevice.cpp @@ -247,6 +247,10 @@ struct VideoCaptureReceiverPin : winrt::implements(pmt)); } diff --git a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp index e938ed02f2..2a860a72fa 100644 --- a/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp +++ b/src/modules/videoconference/VideoConferenceProxyFilter/VideoCaptureProxyFilter.cpp @@ -349,7 +349,7 @@ HRESULT VideoCaptureProxyPin::Get( return E_UNEXPECTED; } - *(GUID*)pPropData = PIN_CATEGORY_CAPTURE; + *static_cast(pPropData) = PIN_CATEGORY_CAPTURE; LOG("VideoCaptureProxyPin::Get SUCCESS"); return S_OK; diff --git a/src/modules/videoconference/VideoConferenceShared/Logging.h b/src/modules/videoconference/VideoConferenceShared/Logging.h index 633656f695..e585759229 100644 --- a/src/modules/videoconference/VideoConferenceShared/Logging.h +++ b/src/modules/videoconference/VideoConferenceShared/Logging.h @@ -41,12 +41,12 @@ std::string toMediaTypeString(GUID subtype); #define LOG(str) LogToFile(str, false); #endif -inline bool failed(HRESULT hr) +constexpr inline bool failed(HRESULT hr) { return hr != S_OK; } -inline bool failed(bool val) +constexpr inline bool failed(bool val) { return val == false; } diff --git a/src/runner/UpdateUtils.cpp b/src/runner/UpdateUtils.cpp index 672cc7e2dd..5de6fa740f 100644 --- a/src/runner/UpdateUtils.cpp +++ b/src/runner/UpdateUtils.cpp @@ -6,6 +6,7 @@ #include "general_settings.h" #include "UpdateUtils.h" +#include #include #include #include @@ -21,6 +22,10 @@ namespace { constexpr int64_t UPDATE_CHECK_INTERVAL_MINUTES = 60 * 24; constexpr int64_t UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES = 60 * 2; + + // How many minor versions to suspend the toast notification (example: installed=0.60.0, suspend=2, next notification=0.63.*) + // Attention: When changing this value please update the ADML file to. + const int UPDATE_NOTIFICATION_TOAST_SUSPEND_MINOR_VERSION_COUNT = 2; } using namespace notifications; using namespace updating; @@ -88,13 +93,32 @@ bool IsMeteredConnection() { using namespace winrt::Windows::Networking::Connectivity; ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile(); - return internetConnectionProfile && internetConnectionProfile.IsWwanConnectionProfile(); + if (!internetConnectionProfile) + { + return false; + } + + if (internetConnectionProfile.IsWwanConnectionProfile()) + { + return true; + } + + ConnectionCost connectionCost = internetConnectionProfile.GetConnectionCost(); + if (connectionCost.Roaming() + || connectionCost.OverDataLimit() + || connectionCost.NetworkCostType() == NetworkCostType::Fixed + || connectionCost.NetworkCostType() == NetworkCostType::Variable) + { + return true; + } + + return false; } void ProcessNewVersionInfo(const github_version_info& version_info, UpdateState& state, const bool download_update, - const bool show_notifications) + bool show_notifications) { state.githubUpdateLastCheckedDate.emplace(timeutil::now()); if (std::holds_alternative(version_info)) @@ -116,6 +140,22 @@ void ProcessNewVersionInfo(const github_version_info& version_info, return; } + // Check notification GPO. + // We check only if notifications are allowed. This is the case if we are triggered by the periodic check. + if (show_notifications && powertoys_gpo::getSuspendNewUpdateToastValue() == powertoys_gpo::gpo_rule_configured_enabled) + { + Logger::info(L"GPO to suspend new update toast notification is enabled."); + if (new_version_info.version.major <= VERSION_MAJOR && new_version_info.version.minor - VERSION_MINOR <= UPDATE_NOTIFICATION_TOAST_SUSPEND_MINOR_VERSION_COUNT) + { + Logger::info(L"The difference between the installed version and the newer version is within the allowed period. The toast notification is not shown."); + show_notifications = false; + } + else + { + Logger::info(L"The installed version is older than allowed for suspending the toast notification. The toast notification is shown."); + } + } + if (download_update) { Logger::trace(L"Downloading installer for a new version"); @@ -149,6 +189,14 @@ void ProcessNewVersionInfo(const github_version_info& version_info, void PeriodicUpdateWorker() { + // Check if periodic update check is disabled by GPO. + // This policy code is implemented but not active. It is for later usage in PT version after 1.0 release. + //if (powertoys_gpo::getDisablePeriodicUpdateCheckValue() == powertoys_gpo::gpo_rule_configured_enabled) + //{ + // Logger::info(L"Initialization of periodic update checks stopped. Periodic update checks are disabled by GPO."); + // return; + //} + for (;;) { auto state = UpdateState::read(); @@ -165,7 +213,14 @@ void PeriodicUpdateWorker() std::this_thread::sleep_for(std::chrono::minutes{ sleep_minutes_till_next_update }); - const bool download_update = !IsMeteredConnection() && get_general_settings().downloadUpdatesAutomatically; + // Auto download setting. + bool download_update = !IsMeteredConnection() && get_general_settings().downloadUpdatesAutomatically; + if (powertoys_gpo::getDisableAutomaticUpdateDownloadValue() == powertoys_gpo::gpo_rule_configured_enabled) + { + Logger::info(L"Automatic download of updates is disabled by GPO."); + download_update = false; + } + bool version_info_obtained = false; try { @@ -211,7 +266,15 @@ void CheckForUpdatesCallback() new_version_info = version_up_to_date{}; Logger::error(L"Couldn't obtain version info from github: {}", new_version_info.error()); } - const bool download_update = !IsMeteredConnection() && get_general_settings().downloadUpdatesAutomatically; + + // Auto download setting + bool download_update = !IsMeteredConnection() && get_general_settings().downloadUpdatesAutomatically; + if (powertoys_gpo::getDisableAutomaticUpdateDownloadValue() == powertoys_gpo::gpo_rule_configured_enabled) + { + Logger::info(L"Automatic download of updates is disabled by GPO."); + download_update = false; + } + ProcessNewVersionInfo(*new_version_info, state, download_update, false); UpdateState::store([&](UpdateState& v) { v = std::move(state); diff --git a/src/runner/auto_start_helper.cpp b/src/runner/auto_start_helper.cpp index e72fa31d78..ebe39d01be 100644 --- a/src/runner/auto_start_helper.cpp +++ b/src/runner/auto_start_helper.cpp @@ -75,7 +75,7 @@ bool create_auto_start_task_for_this_user(bool runElevated) NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -286,7 +286,7 @@ bool delete_auto_start_task_for_this_user() NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. @@ -351,7 +351,7 @@ bool is_auto_start_task_active_for_this_user() NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, - (void**)&pService); + reinterpret_cast(&pService)); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); // Connect to the task service. diff --git a/src/runner/centralized_kb_hook.cpp b/src/runner/centralized_kb_hook.cpp index 02b344b089..fcb96a09a7 100644 --- a/src/runner/centralized_kb_hook.cpp +++ b/src/runner/centralized_kb_hook.cpp @@ -88,6 +88,12 @@ namespace CentralizedKeyboardHook const auto& keyPressInfo = *reinterpret_cast(lParam); + if (keyPressInfo.dwExtraInfo == PowertoyModuleIface::CENTRALIZED_KEYBOARD_HOOK_DONT_TRIGGER_FLAG) + { + // The new keystroke was generated from one of our actions. We should pass it along. + return CallNextHookEx(hHook, nCode, wParam, lParam); + } + // Check if the keys are pressed. if (!pressedKeyDescriptors.empty()) { diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 60f69eccdf..de72d9aa2d 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -16,6 +16,7 @@ static std::wstring settings_theme = L"system"; static bool run_as_elevated = false; static bool download_updates_automatically = true; +static bool enable_experimentation = true; json::JsonObject GeneralSettings::to_json() { @@ -37,6 +38,7 @@ json::JsonObject GeneralSettings::to_json() result.SetNamedValue(L"is_elevated", json::value(isElevated)); result.SetNamedValue(L"run_elevated", json::value(isRunElevated)); result.SetNamedValue(L"download_updates_automatically", json::value(downloadUpdatesAutomatically)); + result.SetNamedValue(L"enable_experimentation", json::value(enableExperimentation)); result.SetNamedValue(L"is_admin", json::value(isAdmin)); result.SetNamedValue(L"theme", json::value(theme)); result.SetNamedValue(L"system_theme", json::value(systemTheme)); @@ -55,6 +57,7 @@ json::JsonObject load_general_settings() } run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false); download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true) && check_user_is_admin(); + enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation",true); return loaded; } @@ -67,6 +70,7 @@ GeneralSettings get_general_settings() .isRunElevated = run_as_elevated, .isAdmin = is_user_admin, .downloadUpdatesAutomatically = download_updates_automatically && is_user_admin, + .enableExperimentation = enable_experimentation, .theme = settings_theme, .systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light", .powerToysVersion = get_product_version() @@ -89,6 +93,8 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save) download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true); + enable_experimentation = general_configs.GetNamedBoolean(L"enable_experimentation", true); + if (json::has(general_configs, L"startup", json::JsonValueType::Boolean)) { const bool startup = general_configs.GetNamedBoolean(L"startup"); diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h index bc7f59cae6..4f87ffd884 100644 --- a/src/runner/general_settings.h +++ b/src/runner/general_settings.h @@ -11,6 +11,7 @@ struct GeneralSettings bool isRunElevated; bool isAdmin; bool downloadUpdatesAutomatically; + bool enableExperimentation; std::wstring theme; std::wstring systemTheme; std::wstring powerToysVersion; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 8d87b63c43..f32723684b 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -158,11 +158,14 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow L"modules/Awake/PowerToys.AwakeModuleInterface.dll", L"modules/MouseUtils/PowerToys.FindMyMouse.dll", L"modules/MouseUtils/PowerToys.MouseHighlighter.dll", + L"modules/MouseUtils/PowerToys.MouseJump.dll", L"modules/AlwaysOnTop/PowerToys.AlwaysOnTopModuleInterface.dll", L"modules/MouseUtils/PowerToys.MousePointerCrosshairs.dll", L"modules/PowerAccent/PowerToys.PowerAccentModuleInterface.dll", L"modules/PowerOCR/PowerToys.PowerOCRModuleInterface.dll", + L"modules/PastePlain/PowerToys.PastePlainModuleInterface.dll", L"modules/FileLocksmith/PowerToys.FileLocksmithExt.dll", + L"modules/RegistryPreview/PowerToys.RegistryPreviewExt.dll", L"modules/MeasureTool/PowerToys.MeasureToolModuleInterface.dll", L"modules/Hosts/PowerToys.HostsModuleInterface.dll", L"modules/Peek/PowerToys.Peek.dll", @@ -204,7 +207,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow { window = winrt::to_hstring(settingsWindow); } - open_settings_window(window); + open_settings_window(window, false); } if (openOobe) diff --git a/src/runner/settings_telemetry.cpp b/src/runner/settings_telemetry.cpp index 3ef22a9c7a..7bab24d448 100644 --- a/src/runner/settings_telemetry.cpp +++ b/src/runner/settings_telemetry.cpp @@ -48,7 +48,7 @@ void send() { powertoy->send_settings_telemetry(); } - catch(...) + catch (...) { Logger::error(L"Failed to send telemetry for {} module", name); } @@ -59,20 +59,20 @@ void send() void run_interval() { auto time = get_last_send_time(); - long long wait_time = 24*3600; + long long wait_time = 24 * 3600; long long left_to_wait = 0; if (time.has_value()) { left_to_wait = max(0, wait_time - timeutil::diff::in_seconds(timeutil::now(), time.value())); } - Sleep((DWORD)left_to_wait * 1000); + Sleep(static_cast(left_to_wait * 1000)); send(); update_last_send_time(timeutil::now()); while (true) { - Sleep((DWORD)wait_time * 1000); + Sleep(static_cast(wait_time * 1000)); send(); update_last_send_time(timeutil::now()); } @@ -87,7 +87,7 @@ void settings_telemetry::init() } catch (...) { - Logger::error("Failed to send settings telemetry"); + Logger::error("Failed to send settings telemetry"); } }).detach(); } diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 340c6f5a70..9dfb269aae 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -11,12 +11,14 @@ #include "restart_elevated.h" #include "UpdateUtils.h" #include "centralized_kb_hook.h" +#include "Generated files/resource.h" #include #include #include #include #include +#include #include #include #include @@ -175,7 +177,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) const std::wstring settings_string{ get_all_settings().Stringify().c_str() }; { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(settings_string); } } @@ -185,7 +187,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) const std::wstring settings_string{ get_all_settings().Stringify().c_str() }; { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(settings_string); } } @@ -194,7 +196,7 @@ void dispatch_received_json(const std::wstring& json_to_parse) const std::wstring settings_string{ get_all_settings().Stringify().c_str() }; { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(settings_string); } } @@ -205,18 +207,42 @@ void dispatch_received_json(const std::wstring& json_to_parse) { { std::unique_lock lock{ ipc_mutex }; - if(current_settings_ipc) + if (current_settings_ipc) current_settings_ipc->send(result.value()); } } } + else if (name == L"bugreport") + { + std::wstring bug_report_path = get_module_folderpath(); + bug_report_path += L"\\Tools\\PowerToys.BugReportTool.exe"; + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE }; + sei.lpFile = bug_report_path.c_str(); + sei.nShow = SW_HIDE; + if (ShellExecuteExW(&sei)) + { + WaitForSingleObject(sei.hProcess, INFINITE); + CloseHandle(sei.hProcess); + static const std::wstring bugreport_success = GET_RESOURCE_STRING(IDS_BUGREPORT_SUCCESS); + MessageBoxW(nullptr, bugreport_success.c_str(), L"PowerToys", MB_OK); + } + } + else if (name == L"killrunner") + { + const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr); + if (pt_main_window != nullptr) + { + SendMessageW(pt_main_window, WM_CLOSE, 0, 0); + } + } } return; } void dispatch_received_json_callback(PVOID data) { - std::wstring* msg = (std::wstring*)data; + std::wstring* msg = static_cast(data); dispatch_received_json(*msg); delete msg; } @@ -290,7 +316,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args, DWORD g_settings_process_id = 0; -void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window) +void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional settings_window, bool show_flyout = false, const std::optional& flyout_position = std::nullopt) { g_isLaunchInProgress = true; @@ -319,7 +345,7 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidCreate can not create guid. {}", val.has_value() ? val.value() : L""); } - else if (UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars) != RPC_S_OK) + else if (UuidToString(&temp_uuid, reinterpret_cast(&uuid_chars)) != RPC_S_OK) { auto val = get_last_error_message(GetLastError()); Logger::warn(L"UuidToString can not convert to string. {}", val.has_value() ? val.value() : L""); @@ -329,7 +355,7 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op { powertoys_pipe_name += std::wstring(uuid_chars); settings_pipe_name += std::wstring(uuid_chars); - RpcStringFree((RPC_WSTR*)&uuid_chars); + RpcStringFree(reinterpret_cast(&uuid_chars)); uuid_chars = nullptr; } @@ -344,8 +370,6 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op settings_theme = L"dark"; } - GeneralSettings save_settings = get_general_settings(); - // Arg 6: elevated status bool isElevated{ get_general_settings().isElevated }; std::wstring settings_elevatedStatus = isElevated ? L"true" : L"false"; @@ -360,20 +384,30 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op // Arg 9: should scoobe window be shown std::wstring settings_showScoobe = show_scoobe_window ? L"true" : L"false"; - // create general settings file to initialize the settings file with installation configurations like : - // 1. Run on start up. - PTSettingsHelper::save_general_settings(save_settings.to_json()); + // Arg 10: should flyout be shown + std::wstring settings_showFlyout = show_flyout ? L"true" : L"false"; - std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {}", - executable_path, - powertoys_pipe_name, - settings_pipe_name, - std::to_wstring(powertoys_pid), - settings_theme, - settings_elevatedStatus, - settings_isUserAnAdmin, - settings_showOobe, - settings_showScoobe); + // Arg 11: contains if there's a settings window argument. If true, will add one extra argument with the value to the call. + std::wstring settings_containsSettingsWindow = settings_window.has_value() ? L"true" : L"false"; + + // Arg 12: contains if there's flyout coordinates. If true, will add two extra arguments to the call containing the x and y coordinates. + std::wstring settings_containsFlyoutPosition = flyout_position.has_value() ? L"true" : L"false"; + + // Args 13, .... : Optional arguments depending on the options presented before. All by the same value. + + std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {} {} {}", + executable_path, + powertoys_pipe_name, + settings_pipe_name, + std::to_wstring(powertoys_pid), + settings_theme, + settings_elevatedStatus, + settings_isUserAnAdmin, + settings_showOobe, + settings_showScoobe, + settings_showFlyout, + settings_containsSettingsWindow, + settings_containsFlyoutPosition); if (settings_window.has_value()) { @@ -381,6 +415,14 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op executable_args.append(settings_window.value()); } + if (flyout_position) + { + executable_args.append(L" "); + executable_args.append(std::to_wstring(flyout_position.value().x)); + executable_args.append(L" "); + executable_args.append(std::to_wstring(flyout_position.value().y)); + } + BOOL process_created = false; // Commented out to fix #22659 @@ -520,18 +562,40 @@ void bring_settings_to_front() EnumWindows(callback, 0); } -void open_settings_window(std::optional settings_window) +void open_settings_window(std::optional settings_window, bool show_flyout = false, const std::optional& flyout_position) { if (g_settings_process_id != 0) { - bring_settings_to_front(); + if (show_flyout) + { + if (current_settings_ipc) + { + if (!flyout_position.has_value()) + { + current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}"); + } + else + { + current_settings_ipc->send(fmt::format(L"{{\"ShowYourself\":\"flyout\", \"x_position\":{}, \"y_position\":{} }}", std::to_wstring(flyout_position.value().x), std::to_wstring(flyout_position.value().y))); + } + } + } + else + { + // nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated) + // bring_settings_to_front(); + if (current_settings_ipc) + { + current_settings_ipc->send(L"{\"ShowYourself\":\"main_page\"}"); + } + } } else { if (!g_isLaunchInProgress) { - std::thread([settings_window]() { - run_settings_window(false, false, settings_window); + std::thread([settings_window, show_flyout, flyout_position]() { + run_settings_window(false, false, settings_window, show_flyout, flyout_position); }).detach(); } } @@ -593,6 +657,8 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value) return "VideoConference"; case ESettingsWindowNames::Hosts: return "Hosts"; + case ESettingsWindowNames::RegistryPreview: + return "RegistryPreview"; default: { Logger::error(L"Can't convert ESettingsWindowNames value={} to string", static_cast(value)); @@ -656,6 +722,10 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value) { return ESettingsWindowNames::Hosts; } + else if (value == "RegistryPreview") + { + return ESettingsWindowNames::RegistryPreview; + } else { Logger::error(L"Can't convert string value={} to ESettingsWindowNames", winrt::to_hstring(value)); diff --git a/src/runner/settings_window.h b/src/runner/settings_window.h index 7e2f9ed89d..1497c2382e 100644 --- a/src/runner/settings_window.h +++ b/src/runner/settings_window.h @@ -16,14 +16,16 @@ enum class ESettingsWindowNames FileExplorer, ShortcutGuide, VideoConference, - Hosts + Hosts, + RegistryPreview, }; std::string ESettingsWindowNames_to_string(ESettingsWindowNames value); ESettingsWindowNames ESettingsWindowNames_from_string(std::string value); -void open_settings_window(std::optional settings_window); +void open_settings_window(std::optional settings_window, bool show_flyout, const std::optional& flyout_position); void close_settings_window(); void open_oobe_window(); void open_scoobe_window(); +void open_flyout(); diff --git a/src/runner/trace.cpp b/src/runner/trace.cpp index 5e5bdcbf38..82522bd0f4 100644 --- a/src/runner/trace.cpp +++ b/src/runner/trace.cpp @@ -56,6 +56,7 @@ void Trace::SettingsChanged(const GeneralSettings& settings) TraceLoggingWideString(enabledModules.c_str(), "ModulesEnabled"), TraceLoggingBoolean(settings.isRunElevated, "AlwaysRunElevated"), TraceLoggingBoolean(settings.downloadUpdatesAutomatically, "DownloadUpdatesAutomatically"), + TraceLoggingBoolean(settings.enableExperimentation, "EnableExperimentation"), TraceLoggingWideString(settings.theme.c_str(), "Theme"), ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index 6fe32df61f..60669f7d7c 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -30,6 +30,10 @@ namespace HMENU h_menu = nullptr; HMENU h_sub_menu = nullptr; + bool double_click_timer_running = false; + bool double_clicked = false; + POINT tray_icon_click_point; + } // Struct to fill with callback and the data. The window_proc is responsible for cleaning it. @@ -49,7 +53,7 @@ bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID wnd_msg->_callback = _callback; wnd_msg->data = data; - PostMessage(tray_icon_hwnd, wm_run_on_main_ui_thread, 0, (LPARAM)wnd_msg); + PostMessage(tray_icon_hwnd, wm_run_on_main_ui_thread, 0, reinterpret_cast(wnd_msg)); return true; } @@ -69,7 +73,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) case ID_SETTINGS_MENU_COMMAND: { std::wstring settings_window{ winrt::to_hstring(ESettingsWindowNames_to_string(static_cast(lparam))) }; - open_settings_window(settings_window); + open_settings_window(settings_window, false); } break; case ID_EXIT_MENU_COMMAND: @@ -89,7 +93,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) } break; case ID_REPORT_BUG_COMMAND: - { + { std::wstring bug_report_path = get_module_folderpath(); bug_report_path += L"\\Tools\\PowerToys.BugReportTool.exe"; SHELLEXECUTEINFOW sei{ sizeof(sei) }; @@ -112,7 +116,15 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam) RunNonElevatedEx(L"https://aka.ms/PowerToysOverview", L"", L""); break; } - + } +} + +void click_timer_elapsed() +{ + double_click_timer_running = false; + if (!double_clicked) + { + open_settings_window(std::nullopt, true, tray_icon_click_point); } } @@ -168,11 +180,6 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam { switch (lparam) { - case WM_LBUTTONDBLCLK: - { - open_settings_window(std::nullopt); - break; - } case WM_RBUTTONUP: case WM_CONTEXTMENU: { @@ -186,7 +193,6 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam static std::wstring exit_menuitem_label = GET_RESOURCE_STRING(IDS_EXIT_MENU_TEXT); static std::wstring submit_bug_menuitem_label = GET_RESOURCE_STRING(IDS_SUBMIT_BUG_TEXT); static std::wstring documentation_menuitem_label = GET_RESOURCE_STRING(IDS_DOCUMENTATION_MENU_TEXT); - change_menu_item_text(ID_SETTINGS_MENU_COMMAND, settings_menuitem_label.data()); change_menu_item_text(ID_EXIT_MENU_COMMAND, exit_menuitem_label.data()); change_menu_item_text(ID_REPORT_BUG_COMMAND, submit_bug_menuitem_label.data()); @@ -200,6 +206,33 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam GetCursorPos(&mouse_pointer); SetForegroundWindow(window); // Needed for the context menu to disappear. TrackPopupMenu(h_sub_menu, TPM_CENTERALIGN | TPM_BOTTOMALIGN, mouse_pointer.x, mouse_pointer.y, 0, window, nullptr); + break; + } + case WM_LBUTTONUP: + { + // ignore event if this is the second click of a double click + if (!double_click_timer_running) + { + // save the cursor position for sending where to show the popup. + GetCursorPos(&tray_icon_click_point); + + // start timer for detecting single or double click + double_click_timer_running = true; + double_clicked = false; + + UINT doubleClickTime = GetDoubleClickTime(); + std::thread([doubleClickTime]() { + std::this_thread::sleep_for(std::chrono::milliseconds(doubleClickTime)); + click_timer_elapsed(); + }).detach(); + } + break; + } + case WM_LBUTTONDBLCLK: + { + double_clicked = true; + open_settings_window(std::nullopt, false); + break; } break; } @@ -208,7 +241,7 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam { if (lparam != NULL) { - struct run_on_main_ui_thread_msg* msg = (struct run_on_main_ui_thread_msg*)lparam; + struct run_on_main_ui_thread_msg* msg = reinterpret_cast(lparam); msg->_callback(msg->data); delete msg; lparam = NULL; diff --git a/src/runner/tray_icon.h b/src/runner/tray_icon.h index e65fa9720f..812f1f1c20 100644 --- a/src/runner/tray_icon.h +++ b/src/runner/tray_icon.h @@ -7,7 +7,7 @@ void start_tray_icon(); // Stop the Tray Icon void stop_tray_icon(); // Open the Settings Window -void open_settings_window(std::optional settings_window); +void open_settings_window(std::optional settings_window, bool show_flyout, const std::optional& flyout_position = std::nullopt); // Callback type to be called by the tray icon loop typedef void (*main_loop_callback_function)(PVOID); // Calls a callback in _callback diff --git a/src/runner/unhandled_exception_handler.cpp b/src/runner/unhandled_exception_handler.cpp index 373d33ea36..1d01349dd3 100644 --- a/src/runner/unhandled_exception_handler.cpp +++ b/src/runner/unhandled_exception_handler.cpp @@ -7,7 +7,7 @@ #include #include -static IMAGEHLP_SYMBOL64* p_symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(WCHAR)); +static IMAGEHLP_SYMBOL64* p_symbol = static_cast(malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(WCHAR))); static IMAGEHLP_LINE64 line; static bool processing_exception = false; static WCHAR module_path[MAX_PATH]; @@ -106,14 +106,14 @@ void log_stack_trace(std::wstring& generalErrorDescription) #else IMAGE_FILE_MACHINE_AMD64, #endif - process, - thread, - &stack, - &context, - NULL, - SymFunctionTableAccess64, - SymGetModuleBase64, - NULL); + process, + thread, + &stack, + &context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL); p_symbol->MaxNameLength = MAX_PATH; p_symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); @@ -126,7 +126,7 @@ void log_stack_trace(std::wstring& generalErrorDescription) auto module_base = SymGetModuleBase64(process, stack.AddrPC.Offset); if (module_base) { - GetModuleFileName((HINSTANCE)module_base, module_path, MAX_PATH); + GetModuleFileName(reinterpret_cast(module_base), module_path, MAX_PATH); } ss << module_path << "!" << p_symbol->Name diff --git a/src/settings-ui/Settings.UI.Library/AwakeProperties.cs b/src/settings-ui/Settings.UI.Library/AwakeProperties.cs index 6db754d12b..6e9735d7b6 100644 --- a/src/settings-ui/Settings.UI.Library/AwakeProperties.cs +++ b/src/settings-ui/Settings.UI.Library/AwakeProperties.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Text.Json.Serialization; @@ -13,25 +14,29 @@ namespace Microsoft.PowerToys.Settings.UI.Library { KeepDisplayOn = false; Mode = AwakeMode.PASSIVE; - Hours = 0; - Minutes = 0; - TrayTimeShortcuts = new Dictionary(); + IntervalHours = 0; + IntervalMinutes = 0; + ExpirationDateTime = DateTimeOffset.Now; + CustomTrayTimes = new Dictionary(); } - [JsonPropertyName("awake_keep_display_on")] + [JsonPropertyName("keepDisplayOn")] public bool KeepDisplayOn { get; set; } - [JsonPropertyName("awake_mode")] + [JsonPropertyName("mode")] public AwakeMode Mode { get; set; } - [JsonPropertyName("awake_hours")] - public uint Hours { get; set; } + [JsonPropertyName("intervalHours")] + public uint IntervalHours { get; set; } - [JsonPropertyName("awake_minutes")] - public uint Minutes { get; set; } + [JsonPropertyName("intervalMinutes")] + public uint IntervalMinutes { get; set; } - [JsonPropertyName("tray_times")] - public Dictionary TrayTimeShortcuts { get; set; } + [JsonPropertyName("expirationDateTime")] + public DateTimeOffset ExpirationDateTime { get; set; } + + [JsonPropertyName("customTrayTimes")] + public Dictionary CustomTrayTimes { get; set; } } public enum AwakeMode @@ -39,5 +44,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library PASSIVE = 0, INDEFINITE = 1, TIMED = 2, + EXPIRABLE = 3, } } diff --git a/src/settings-ui/Settings.UI.Library/AwakeSettings.cs b/src/settings-ui/Settings.UI.Library/AwakeSettings.cs index b14c532ff5..f811dbca37 100644 --- a/src/settings-ui/Settings.UI.Library/AwakeSettings.cs +++ b/src/settings-ui/Settings.UI.Library/AwakeSettings.cs @@ -2,15 +2,17 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Linq; using System.Text.Json.Serialization; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library { - public class AwakeSettings : BasePTModuleSettings, ISettingsConfig + public class AwakeSettings : BasePTModuleSettings, ISettingsConfig, ICloneable { public const string ModuleName = "Awake"; - public const string ModuleVersion = "0.0.1"; + public const string ModuleVersion = "0.0.2"; public AwakeSettings() { @@ -22,6 +24,26 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("properties")] public AwakeProperties Properties { get; set; } + public object Clone() + { + return new AwakeSettings() + { + Name = Name, + Version = Version, + Properties = new AwakeProperties() + { + CustomTrayTimes = Properties.CustomTrayTimes.ToDictionary(entry => entry.Key, entry => entry.Value), + Mode = Properties.Mode, + KeepDisplayOn = Properties.KeepDisplayOn, + IntervalMinutes = Properties.IntervalMinutes, + IntervalHours = Properties.IntervalHours, + + // Fix old buggy default value that might be saved in Settings. Some components don't deal well with negative time zones and minimum time offsets. + ExpirationDateTime = Properties.ExpirationDateTime.Year < 2 ? DateTimeOffset.Now : Properties.ExpirationDateTime, + }, + }; + } + public string GetModuleName() { return Name; diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index 37c6e2b7e4..dcf7cb05ac 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -2,6 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; @@ -12,6 +13,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library { public class EnabledModules { + private Action notifyEnabledChangedAction; + public EnabledModules() { } @@ -28,6 +31,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); fancyZones = value; + NotifyChange(); } } } @@ -76,6 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); shortcutGuide = value; + NotifyChange(); } } } @@ -139,6 +144,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); powerLauncher = value; + NotifyChange(); } } } @@ -155,6 +161,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); colorPicker = value; + NotifyChange(); } } } @@ -207,6 +214,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + private bool mouseJump = true; + + [JsonPropertyName("MouseJump")] + public bool MouseJump + { + get => mouseJump; + set + { + if (mouseJump != value) + { + LogTelemetryEvent(value); + mouseJump = value; + } + } + } + private bool alwaysOnTop = true; [JsonPropertyName("AlwaysOnTop")] @@ -267,6 +290,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); powerOCR = value; + NotifyChange(); + } + } + } + + private bool pastePlain = true; + + [JsonPropertyName("PastePlain")] + public bool PastePlain + { + get => pastePlain; + set + { + if (pastePlain != value) + { + LogTelemetryEvent(value); + pastePlain = value; + NotifyChange(); } } } @@ -283,6 +324,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); measureTool = value; + NotifyChange(); } } } @@ -299,6 +341,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { LogTelemetryEvent(value); hosts = value; + NotifyChange(); } } } @@ -335,6 +378,27 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + private bool registryPreview = true; + + [JsonPropertyName("RegistryPreview")] + public bool RegistryPreview + { + get => registryPreview; + set + { + if (registryPreview != value) + { + LogTelemetryEvent(value); + registryPreview = value; + } + } + } + + private void NotifyChange() + { + notifyEnabledChangedAction?.Invoke(); + } + public string ToJsonString() { return JsonSerializer.Serialize(this); @@ -349,5 +413,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library }; PowerToysTelemetry.Log.WriteEvent(dataEvent); } + + internal void AddEnabledModuleChangeNotification(Action callBack) + { + notifyEnabledChangedAction = callBack; + } } } diff --git a/src/settings-ui/Settings.UI.Library/Enumerations/SvgPreviewCheckeredShade.cs b/src/settings-ui/Settings.UI.Library/Enumerations/SvgPreviewCheckeredShade.cs new file mode 100644 index 0000000000..f27865f9e4 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Enumerations/SvgPreviewCheckeredShade.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Settings.UI.Library.Enumerations +{ + public enum SvgPreviewCheckeredShade + { + Light, + Medium, + Dark, + } +} diff --git a/src/settings-ui/Settings.UI.Library/Enumerations/SvgPreviewColorMode.cs b/src/settings-ui/Settings.UI.Library/Enumerations/SvgPreviewColorMode.cs new file mode 100644 index 0000000000..a6aa3098f0 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Enumerations/SvgPreviewColorMode.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Settings.UI.Library.Enumerations +{ + public enum SvgPreviewColorMode + { + Default, + SolidColor, + Checkered, + } +} diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index be57f43c8b..1de3c04030 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -49,12 +50,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("download_updates_automatically")] public bool AutoDownloadUpdates { get; set; } + [JsonPropertyName("enable_experimentation")] + public bool EnableExperimentation { get; set; } + public GeneralSettings() { Startup = false; IsAdmin = false; IsElevated = false; AutoDownloadUpdates = false; + EnableExperimentation = true; Theme = "system"; SystemTheme = "light"; try @@ -108,5 +113,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library return false; } + + public void AddEnabledModuleChangeNotification(Action callBack) + { + Enabled.AddEnabledModuleChangeNotification(callBack); + } } } diff --git a/src/settings-ui/Settings.UI.Library/HostsProperties.cs b/src/settings-ui/Settings.UI.Library/HostsProperties.cs index b2dae9a5e4..32d1b334c2 100644 --- a/src/settings-ui/Settings.UI.Library/HostsProperties.cs +++ b/src/settings-ui/Settings.UI.Library/HostsProperties.cs @@ -15,12 +15,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonConverter(typeof(BoolPropertyJsonConverter))] public bool LaunchAdministrator { get; set; } + [JsonConverter(typeof(BoolPropertyJsonConverter))] + public bool LoopbackDuplicates { get; set; } + public AdditionalLinesPosition AdditionalLinesPosition { get; set; } public HostsProperties() { ShowStartupWarning = true; LaunchAdministrator = true; + LoopbackDuplicates = false; AdditionalLinesPosition = AdditionalLinesPosition.Top; } } diff --git a/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs b/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs index dbb619ea73..d405d52d00 100644 --- a/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs +++ b/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs @@ -60,7 +60,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library private bool FilterKeyboardEvents(KeyboardEvent ev) { +#pragma warning disable CA2020 // Prevent from behavioral change return _filterKeyboardEvent(ev.key, (UIntPtr)ev.dwExtraInfo); +#pragma warning restore CA2020 // Prevent from behavioral change } protected virtual void Dispose(bool disposing) @@ -77,6 +79,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + public bool GetDisposedState() => disposedValue; + public void Dispose() { Dispose(disposing: true); diff --git a/src/settings-ui/Settings.UI.Library/Interfaces/ISettingsRepository`1.cs b/src/settings-ui/Settings.UI.Library/Interfaces/ISettingsRepository`1.cs index 221a5d04a1..a9cd92899a 100644 --- a/src/settings-ui/Settings.UI.Library/Interfaces/ISettingsRepository`1.cs +++ b/src/settings-ui/Settings.UI.Library/Interfaces/ISettingsRepository`1.cs @@ -7,5 +7,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces public interface ISettingsRepository { T SettingsConfig { get; set; } + + bool ReloadSettings(); } } diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs new file mode 100644 index 0000000000..524039b06e --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs @@ -0,0 +1,23 @@ +// 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. + +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class MouseJumpProperties + { + [JsonPropertyName("activation_shortcut")] + public HotkeySettings ActivationShortcut { get; set; } + + [JsonPropertyName("thumbnail_size")] + public MouseJumpThumbnailSize ThumbnailSize { get; set; } + + public MouseJumpProperties() + { + ActivationShortcut = new HotkeySettings(true, false, false, true, 0x44); + ThumbnailSize = new MouseJumpThumbnailSize(); + } + } +} diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs b/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs new file mode 100644 index 0000000000..73b15f5086 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs @@ -0,0 +1,35 @@ +// 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. + +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class MouseJumpSettings : BasePTModuleSettings, ISettingsConfig + { + public const string ModuleName = "MouseJump"; + + [JsonPropertyName("properties")] + public MouseJumpProperties Properties { get; set; } + + public MouseJumpSettings() + { + Name = ModuleName; + Properties = new MouseJumpProperties(); + Version = "1.0"; + } + + public string GetModuleName() + { + return Name; + } + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + { + return false; + } + } +} diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs b/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs new file mode 100644 index 0000000000..58af7d3683 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/MouseJumpThumbnailSize.cs @@ -0,0 +1,68 @@ +// 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. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class MouseJumpThumbnailSize : INotifyPropertyChanged + { + private int _width; + private int _height; + + [JsonPropertyName("width")] + public int Width + { + get + { + return _width; + } + + set + { + var newWidth = Math.Max(0, value); + if (newWidth != _width) + { + _width = newWidth; + OnPropertyChanged(); + } + } + } + + [JsonPropertyName("height")] + public int Height + { + get + { + return _height; + } + + set + { + var newHeight = Math.Max(0, value); + if (newHeight != _height) + { + _height = newHeight; + OnPropertyChanged(); + } + } + } + + public MouseJumpThumbnailSize() + { + Width = 1600; + Height = 1200; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/settings-ui/Settings.UI.Library/PastePlainProperties.cs b/src/settings-ui/Settings.UI.Library/PastePlainProperties.cs new file mode 100644 index 0000000000..5e4fcbcd14 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PastePlainProperties.cs @@ -0,0 +1,21 @@ +// 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. + +using System.Text.Json; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PastePlainProperties + { + public PastePlainProperties() + { + ActivationShortcut = new HotkeySettings(true, true, true, false, 0x56); // Ctrl+Win+Alt+V + } + + public HotkeySettings ActivationShortcut { get; set; } + + public override string ToString() + => JsonSerializer.Serialize(this); + } +} diff --git a/src/settings-ui/Settings.UI.Library/PastePlainSettings.cs b/src/settings-ui/Settings.UI.Library/PastePlainSettings.cs new file mode 100644 index 0000000000..bf0ee98cfe --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PastePlainSettings.cs @@ -0,0 +1,49 @@ +// 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. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PastePlainSettings : BasePTModuleSettings, ISettingsConfig + { + public const string ModuleName = "PastePlain"; + + [JsonPropertyName("properties")] + public PastePlainProperties Properties { get; set; } + + public PastePlainSettings() + { + Properties = new PastePlainProperties(); + Version = "1"; + Name = ModuleName; + } + + public virtual void Save(ISettingsUtils settingsUtils) + { + // Save settings to file + var options = new JsonSerializerOptions + { + WriteIndented = true, + }; + + if (settingsUtils == null) + { + throw new ArgumentNullException(nameof(settingsUtils)); + } + + settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), ModuleName); + } + + public string GetModuleName() + => Name; + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + => false; + } +} diff --git a/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs b/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs index c0720c65a0..d495203e4e 100644 --- a/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs @@ -72,6 +72,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("search_wait_for_slow_results")] public bool SearchWaitForSlowResults { get; set; } + [JsonPropertyName("generate_thumbnails_from_files")] + public bool GenerateThumbnailsFromFiles { get; set; } + public PowerLauncherProperties() { OpenPowerLauncher = new HotkeySettings(false, false, true, false, 32); @@ -92,6 +95,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library SearchClickedItemWeight = 5; SearchQueryTuningEnabled = false; SearchWaitForSlowResults = false; + GenerateThumbnailsFromFiles = true; } } } diff --git a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs index eee0800e48..1167325cce 100644 --- a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs @@ -7,12 +7,17 @@ using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.PowerToys.Settings.Telemetry; using Microsoft.PowerToys.Telemetry; +using Settings.UI.Library.Enumerations; namespace Microsoft.PowerToys.Settings.UI.Library { public class PowerPreviewProperties { public const string DefaultStlThumbnailColor = "#FFC924"; + public const int DefaultMonacoMaxFileSize = 50; + public const int DefaultSvgBackgroundColorMode = (int)SvgPreviewColorMode.Default; + public const string DefaultSvgBackgroundSolidColor = "#FFFFFF"; + public const int DefaultSvgBackgroundCheckeredShade = (int)SvgPreviewCheckeredShade.Light; private bool enableSvgPreview = true; @@ -31,6 +36,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + [JsonPropertyName("svg-previewer-background-color-mode")] + public IntProperty SvgBackgroundColorMode { get; set; } + + [JsonPropertyName("svg-previewer-background-solid-color")] + public StringProperty SvgBackgroundSolidColor { get; set; } + + [JsonPropertyName("svg-previewer-background-checkered-shade")] + public IntProperty SvgBackgroundCheckeredShade { get; set; } + private bool enableSvgThumbnail = true; [JsonPropertyName("svg-thumbnail-toggle-setting")] @@ -116,6 +130,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } + [JsonPropertyName("monaco-previewer-max-file-size")] + public IntProperty MonacoPreviewMaxFileSize { get; set; } + private bool enablePdfPreview; [JsonPropertyName("pdf-previewer-toggle-setting")] @@ -206,7 +223,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library public PowerPreviewProperties() { + SvgBackgroundColorMode = new IntProperty(DefaultSvgBackgroundColorMode); + SvgBackgroundSolidColor = new StringProperty(DefaultSvgBackgroundSolidColor); + SvgBackgroundCheckeredShade = new IntProperty(DefaultSvgBackgroundCheckeredShade); StlThumbnailColor = new StringProperty(DefaultStlThumbnailColor); + MonacoPreviewMaxFileSize = new IntProperty(DefaultMonacoMaxFileSize); } public override string ToString() diff --git a/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs b/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs new file mode 100644 index 0000000000..92718d2e91 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs @@ -0,0 +1,27 @@ +// 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. + +using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class RegistryPreviewSettings : BasePTModuleSettings, ISettingsConfig + { + public const string ModuleName = "RegistryPreview"; + + public RegistryPreviewSettings() + { + Version = "1"; + Name = ModuleName; + } + + public string GetModuleName() + => Name; + + // This can be utilized in the future if the settings.json file is to be modified/deleted. + public bool UpgradeSettingsConfiguration() + => false; + } +} diff --git a/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj b/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj index 451423bc54..d530c644b5 100644 --- a/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj +++ b/src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs index eeaa0aa541..1b2dc81fb7 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs @@ -15,6 +15,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Threading; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.Library @@ -46,7 +47,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library } } - private class JsonMergeHelper + private sealed class JsonMergeHelper { // mostly from https://stackoverflow.com/questions/58694837/system-text-json-merge-two-objects // but with some update to prevent array item duplicates @@ -308,10 +309,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library var retoreFullPath = Path.Combine(appBasePath, relativePath); var settingsToRestoreJson = GetExportVersion(backupRetoreSettings, currentFile.Key, currentFile.Value); - if (currentSettingsFiles.ContainsKey(currentFile.Key)) + if (currentSettingsFiles.TryGetValue(currentFile.Key, out string value)) { // we have a setting file to restore to - var currentSettingsFileJson = GetExportVersion(backupRetoreSettings, currentFile.Key, currentSettingsFiles[currentFile.Key]); + var currentSettingsFileJson = GetExportVersion(backupRetoreSettings, currentFile.Key, value); if (JsonNormalizer.Normalize(settingsToRestoreJson) != JsonNormalizer.Normalize(currentSettingsFileJson)) { @@ -659,10 +660,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library var currentSettingsFileToBackup = GetExportVersion(backupRetoreSettings, currentFile.Key, currentFile.Value); var doBackup = false; - if (lastBackupSettingsFiles.ContainsKey(currentFile.Key)) + if (lastBackupSettingsFiles.TryGetValue(currentFile.Key, out string value)) { // there is a previous backup for this, get an export version of it. - var lastSettingsFileDoc = GetExportVersion(backupRetoreSettings, currentFile.Key, lastBackupSettingsFiles[currentFile.Key]); + var lastSettingsFileDoc = GetExportVersion(backupRetoreSettings, currentFile.Key, value); // check to see if the new export version would be same as last export version. if (JsonNormalizer.Normalize(currentSettingsFileToBackup) != JsonNormalizer.Normalize(lastSettingsFileDoc)) @@ -989,7 +990,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library /// Class JsonNormalizer is a utility class to 'normalize' a JSON file so that it can be compared to another JSON file. /// This really just means to fully sort it. This does not work for any JSON file where the order of the node is relevant. /// - private class JsonNormalizer + private sealed class JsonNormalizer { public static string Normalize(string json) { diff --git a/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs b/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs index 9cc9931284..b9d27dedb9 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs @@ -44,6 +44,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library { } + public bool ReloadSettings() + { + try + { + T settingsItem = new T(); + settingsConfig = _settingsUtils.GetSettingsOrDefault(settingsItem.GetModuleName()); + + SettingsConfig = settingsConfig; + + return true; + } + catch + { + return false; + } + } + // Settings configurations shared across all viewmodels public T SettingsConfig { diff --git a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs index e09687ff77..a77f9e2c62 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs @@ -6,8 +6,8 @@ using System; using System.IO; using System.IO.Abstractions; using System.Text.Json; +using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; -using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.Library { diff --git a/src/settings-ui/Settings.UI.Library/SndMouseJumpSettings.cs b/src/settings-ui/Settings.UI.Library/SndMouseJumpSettings.cs new file mode 100644 index 0000000000..a19c4a0749 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/SndMouseJumpSettings.cs @@ -0,0 +1,29 @@ +// 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. + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class SndMouseJumpSettings + { + [JsonPropertyName("MouseJump")] + public MouseJumpSettings MouseJump { get; set; } + + public SndMouseJumpSettings() + { + } + + public SndMouseJumpSettings(MouseJumpSettings settings) + { + MouseJump = settings; + } + + public string ToJsonString() + { + return JsonSerializer.Serialize(this); + } + } +} diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs new file mode 100644 index 0000000000..8f17b58dc3 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs @@ -0,0 +1,21 @@ +// 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. + +using System; +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events +{ + [EventData] + public class OobeVariantAssignmentEvent : EventBase, IEvent + { + public string AssignmentContext { get; set; } + + public string ClientID { get; set; } + + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs new file mode 100644 index 0000000000..8e075aea0c --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs @@ -0,0 +1,16 @@ +// 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. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events +{ + [EventData] + public class TrayFlyoutActivatedEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs new file mode 100644 index 0000000000..c1813e9adb --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs @@ -0,0 +1,18 @@ +// 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. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events +{ + [EventData] + public class TrayFlyoutModuleRunEvent : EventBase, IEvent + { + public string ModuleName { get; set; } + + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs index d6432f9efe..00eea891c1 100644 --- a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs @@ -33,6 +33,17 @@ namespace Microsoft.PowerToys.Settings.UI.Library Code = 65, }); + this.PushToTalkMicrophoneHotkey = new KeyboardKeysProperty( + new HotkeySettings() + { + Win = true, + Ctrl = false, + Alt = false, + Shift = true, + Key = "I", + Code = 73, + }); + this.MuteCameraHotkey = new KeyboardKeysProperty( new HotkeySettings() { @@ -43,6 +54,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library Key = "O", Code = 79, }); + + this.PushToReverseEnabled = new BoolProperty(false); } [JsonPropertyName("mute_camera_and_microphone_hotkey")] @@ -51,6 +64,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("mute_microphone_hotkey")] public KeyboardKeysProperty MuteMicrophoneHotkey { get; set; } + [JsonPropertyName("push_to_talk_microphone_hotkey")] + public KeyboardKeysProperty PushToTalkMicrophoneHotkey { get; set; } + + [JsonPropertyName("push_to_reverse_enabled")] + public BoolProperty PushToReverseEnabled { get; set; } + [JsonPropertyName("mute_camera_hotkey")] public KeyboardKeysProperty MuteCameraHotkey { get; set; } diff --git a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs index 1a702b38ae..f7b25470e2 100644 --- a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs +++ b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs @@ -20,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility // Using Ordinal since this is used internally for a path private static readonly Expression> SettingsFilterExpression = s => s == null || s.Contains("Microsoft\\PowerToys\\settings.json", StringComparison.Ordinal); - internal class MockSettingsRepository : ISettingsRepository + internal sealed class MockSettingsRepository : ISettingsRepository where T : ISettingsConfig, new() { private readonly ISettingsUtils _settingsUtils; @@ -48,6 +48,23 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility } } } + + public bool ReloadSettings() + { + try + { + T settingsItem = new T(); + _settingsConfig = _settingsUtils.GetSettingsOrDefault(settingsItem.GetModuleName()); + + SettingsConfig = _settingsConfig; + + return true; + } + catch + { + return false; + } + } } public static Mock GetModuleIOProvider(string version, string module, string fileName) diff --git a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs index 56b7701c9f..62e7f7b9af 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs @@ -100,7 +100,7 @@ namespace CommonLibTest .Select(s => s[random.Next(s.Length)]).ToArray()); } - private partial class TestClass : ISettingsConfig + private sealed partial class TestClass : ISettingsConfig { public int TestInt { get; set; } = 100; diff --git a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj index 70f2f985f0..495d233dec 100644 --- a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj +++ b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj @@ -3,7 +3,7 @@ net7.0-windows10.0.19041.0 false - + win10-x64;win10-arm64 $(Version).0 @@ -24,12 +24,12 @@ - - - - - - + + + + + + diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs index 20f809ac6b..db54fa96dc 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs @@ -14,7 +14,7 @@ namespace ViewModelTests [TestClass] public class PowerLauncherViewModelTest { - private class SendCallbackMock + private sealed class SendCallbackMock { public int TimesSent { get; set; } @@ -80,6 +80,7 @@ namespace ViewModelTests Assert.AreEqual(originalSettings.Properties.OverrideWinkeyS, viewModel.OverrideWinSKey); Assert.AreEqual(originalSettings.Properties.SearchResultPreference, viewModel.SearchResultPreference); Assert.AreEqual(originalSettings.Properties.SearchTypePreference, viewModel.SearchTypePreference); + Assert.AreEqual(originalSettings.Properties.GenerateThumbnailsFromFiles, viewModel.GenerateThumbnailsFromFiles); // Verify that the stub file was used var expectedCallCount = 2; // once via the view model, and once by the test (GetSettings) diff --git a/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs b/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs index 2c401c4a39..c501a23e6b 100644 --- a/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs +++ b/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs @@ -9,7 +9,7 @@ using Windows.ApplicationModel.Activation; namespace Microsoft.PowerToys.Settings.UI.Activation { - internal class DefaultActivationHandler : ActivationHandler + internal sealed class DefaultActivationHandler : ActivationHandler { private readonly Type navElement; diff --git a/src/settings-ui/Settings.UI/App.xaml b/src/settings-ui/Settings.UI/App.xaml index 3858e4f7d2..b4e581d64d 100644 --- a/src/settings-ui/Settings.UI/App.xaml +++ b/src/settings-ui/Settings.UI/App.xaml @@ -3,6 +3,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:local="using:Microsoft.PowerToys.Settings.UI" xmlns:converters="using:CommunityToolkit.WinUI.UI.Converters" xmlns:labs="using:CommunityToolkit.Labs.WinUI"> diff --git a/src/settings-ui/Settings.UI/App.xaml.cs b/src/settings-ui/Settings.UI/App.xaml.cs index 7f10ecf0b1..007482ee92 100644 --- a/src/settings-ui/Settings.UI/App.xaml.cs +++ b/src/settings-ui/Settings.UI/App.xaml.cs @@ -13,6 +13,7 @@ using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; using Microsoft.PowerToys.Settings.UI.Library.Utilities; +using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml; using Windows.UI.Popups; @@ -35,12 +36,13 @@ namespace Microsoft.PowerToys.Settings.UI IsUserAdmin, ShowOobeWindow, ShowScoobeWindow, - SettingsWindow, + ShowFlyout, + ContainsSettingsWindow, + ContainsFlyoutPosition, } // Quantity of arguments - private const int RequiredArgumentsQty = 9; - private const int RequiredAndOptionalArgumentsQty = 10; + private const int RequiredArgumentsQty = 12; // Create an instance of the IPC wrapper. private static TwoWayPipeMessageIPCManaged ipcmanager; @@ -53,6 +55,8 @@ namespace Microsoft.PowerToys.Settings.UI public bool ShowOobe { get; set; } + public bool ShowFlyout { get; set; } + public bool ShowScoobe { get; set; } public Type StartupPage { get; set; } = typeof(Views.GeneralPage); @@ -68,18 +72,29 @@ namespace Microsoft.PowerToys.Settings.UI /// public App() { + Logger.InitializeLogger("\\Settings\\Logs"); + this.InitializeComponent(); } - public static void OpenSettingsWindow(Type type) + public static void OpenSettingsWindow(Type type = null, bool ensurePageIsSelected = false) { if (settingsWindow == null) { settingsWindow = new MainWindow(IsDarkTheme()); + type = typeof(GeneralPage); } settingsWindow.Activate(); - settingsWindow.NavigateToSection(type); + if (type != null) + { + settingsWindow.NavigateToSection(type); + } + + if (ensurePageIsSelected) + { + settingsWindow.EnsurePageIsSelected(); + } } /// @@ -90,6 +105,7 @@ namespace Microsoft.PowerToys.Settings.UI protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { var cmdArgs = Environment.GetCommandLineArgs(); + var isDark = IsDarkTheme(); if (cmdArgs != null && cmdArgs.Length >= RequiredArgumentsQty) @@ -107,11 +123,17 @@ namespace Microsoft.PowerToys.Settings.UI IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true"; ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true"; ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true"; + ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true"; + bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true"; + bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true"; - if (cmdArgs.Length == RequiredAndOptionalArgumentsQty) + // To keep track of variable arguments + int currentArgumentIndex = RequiredArgumentsQty; + + if (containsSettingsWindow) { // open specific window - switch (cmdArgs[(int)Arguments.SettingsWindow]) + switch (cmdArgs[currentArgumentIndex]) { case "Overview": StartupPage = typeof(Views.GeneralPage); break; case "AlwaysOnTop": StartupPage = typeof(Views.AlwaysOnTopPage); break; @@ -131,8 +153,21 @@ namespace Microsoft.PowerToys.Settings.UI case "VideoConference": StartupPage = typeof(Views.VideoConferencePage); break; case "MeasureTool": StartupPage = typeof(Views.MeasureToolPage); break; case "Hosts": StartupPage = typeof(Views.HostsPage); break; + case "RegistryPreview": StartupPage = typeof(Views.RegistryPreviewPage); break; + case "PastePlain": StartupPage = typeof(Views.PastePlainPage); break; default: Debug.Assert(false, "Unexpected SettingsWindow argument value"); break; } + + currentArgumentIndex++; + } + + int flyout_x = 0; + int flyout_y = 0; + if (containsFlyoutPosition) + { + // get the flyout position arguments + _ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x); + _ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y); } RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () => @@ -149,7 +184,7 @@ namespace Microsoft.PowerToys.Settings.UI }); ipcmanager.Start(); - if (!ShowOobe && !ShowScoobe) + if (!ShowOobe && !ShowScoobe && !ShowFlyout) { settingsWindow = new MainWindow(isDark); settingsWindow.Activate(); @@ -176,6 +211,16 @@ namespace Microsoft.PowerToys.Settings.UI scoobeWindow.Activate(); SetOobeWindow(scoobeWindow); } + else if (ShowFlyout) + { + POINT? p = null; + if (containsFlyoutPosition) + { + p = new POINT(flyout_x, flyout_y); + } + + ShellPage.OpenFlyoutCallback(p); + } } } else @@ -277,6 +322,7 @@ namespace Microsoft.PowerToys.Settings.UI private static MainWindow settingsWindow; private static OobeWindow oobeWindow; + private static FlyoutWindow flyoutWindow; private static ThemeListener themeListener; public static void ClearSettingsWindow() @@ -294,14 +340,29 @@ namespace Microsoft.PowerToys.Settings.UI return oobeWindow; } + public static FlyoutWindow GetFlyoutWindow() + { + return flyoutWindow; + } + public static void SetOobeWindow(OobeWindow window) { oobeWindow = window; } + public static void SetFlyoutWindow(FlyoutWindow window) + { + flyoutWindow = window; + } + public static void ClearOobeWindow() { oobeWindow = null; } + + public static void ClearFlyoutWindow() + { + flyoutWindow = null; + } } } diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsMouseJump.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsMouseJump.png new file mode 100644 index 0000000000..15568110cc Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsMouseJump.png differ diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPastePlain.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPastePlain.png new file mode 100644 index 0000000000..94f89c88e1 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsPastePlain.png differ diff --git a/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsRegistryPreview.png b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsRegistryPreview.png new file mode 100644 index 0000000000..887c42a182 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/FluentIcons/FluentIconsRegistryPreview.png differ diff --git a/src/settings-ui/Settings.UI/Assets/Modules/OOBE/OOBEPTHeroShort.png b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/OOBEPTHeroShort.png new file mode 100644 index 0000000000..085b113599 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/OOBEPTHeroShort.png differ diff --git a/src/settings-ui/Settings.UI/Assets/Modules/OOBE/PastePlain.gif b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/PastePlain.gif new file mode 100644 index 0000000000..a9f6c089b7 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/PastePlain.gif differ diff --git a/src/settings-ui/Settings.UI/Assets/Modules/OOBE/RegistryPreview.png b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/RegistryPreview.png new file mode 100644 index 0000000000..754005de39 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/Modules/OOBE/RegistryPreview.png differ diff --git a/src/settings-ui/Settings.UI/Assets/Modules/PastePlain.png b/src/settings-ui/Settings.UI/Assets/Modules/PastePlain.png new file mode 100644 index 0000000000..cd5860fe7f Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/Modules/PastePlain.png differ diff --git a/src/settings-ui/Settings.UI/Assets/Modules/RegistryPreview.png b/src/settings-ui/Settings.UI/Assets/Modules/RegistryPreview.png new file mode 100644 index 0000000000..4afbf66f06 Binary files /dev/null and b/src/settings-ui/Settings.UI/Assets/Modules/RegistryPreview.png differ diff --git a/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs new file mode 100644 index 0000000000..17c901cbba --- /dev/null +++ b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.cs @@ -0,0 +1,43 @@ +// 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. + +using CommunityToolkit.Labs.WinUI; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using WinUIEx; + +namespace Microsoft.PowerToys.Settings.UI.Controls +{ + public partial class FlyoutMenuButton : Button + { + /// + /// The backing for the property. + /// + public static readonly DependencyProperty IconProperty = DependencyProperty.Register( + nameof(Icon), + typeof(object), + typeof(FlyoutMenuButton), + new PropertyMetadata(defaultValue: null)); + + /// + /// Gets or sets the icon. + /// + public object Icon + { + get => (object)GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public FlyoutMenuButton() + { + this.DefaultStyleKey = typeof(FlyoutMenuButton); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml new file mode 100644 index 0000000000..e9b3a6a55f --- /dev/null +++ b/src/settings-ui/Settings.UI/Controls/FlyoutMenuButton/FlyoutMenuButton.xaml @@ -0,0 +1,104 @@ + + + + + + + diff --git a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml index 391026cee4..98877bd01c 100644 --- a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml +++ b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml @@ -10,12 +10,13 @@ - + @@ -23,9 +24,7 @@ Grid.Row="1" Padding="32,24,32,24" VerticalScrollBarVisibility="Auto"> - + - + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs index abc73ee3e2..72abab90d6 100644 --- a/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs +++ b/src/settings-ui/Settings.UI/Controls/OOBEPageControl/OOBEPageControl.xaml.cs @@ -32,6 +32,12 @@ namespace Microsoft.PowerToys.Settings.UI.Controls set => SetValue(HeroImageProperty, value); } + public double HeroImageHeight + { + get { return (double)GetValue(HeroImageHeightProperty); } + set { SetValue(HeroImageHeightProperty, value); } + } + public object PageContent { get { return (object)GetValue(PageContentProperty); } @@ -42,5 +48,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register("Description", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string))); public static readonly DependencyProperty HeroImageProperty = DependencyProperty.Register("HeroImage", typeof(string), typeof(SettingsPageControl), new PropertyMetadata(default(string))); public static readonly DependencyProperty PageContentProperty = DependencyProperty.Register("PageContent", typeof(object), typeof(SettingsPageControl), new PropertyMetadata(new Grid())); + public static readonly DependencyProperty HeroImageHeightProperty = DependencyProperty.Register("HeroImageHeight", typeof(double), typeof(SettingsPageControl), new PropertyMetadata(280.0)); } } diff --git a/src/settings-ui/Settings.UI/Controls/SettingsPageControl/SettingsPageControl.xaml b/src/settings-ui/Settings.UI/Controls/SettingsPageControl/SettingsPageControl.xaml index 549e92c44d..1252811ccf 100644 --- a/src/settings-ui/Settings.UI/Controls/SettingsPageControl/SettingsPageControl.xaml +++ b/src/settings-ui/Settings.UI/Controls/SettingsPageControl/SettingsPageControl.xaml @@ -19,14 +19,30 @@ TrueValue="Visible" /> - + - + + + + + + + + + + + + + + + + + + + - - @@ -51,12 +67,19 @@ + ColumnSpacing="16" + RowSpacing="16"> - + + + + + @@ -66,7 +89,7 @@ - + - - + + - + @@ -127,20 +144,14 @@ ItemsSource="{x:Bind SecondaryLinks}"> - - + + - + diff --git a/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs b/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs index 52f6750684..c9b9035049 100644 --- a/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs +++ b/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutControl.xaml.cs @@ -89,6 +89,11 @@ namespace Microsoft.PowerToys.Settings.UI.Controls hook = new HotkeySettingsControlHook(Hotkey_KeyDown, Hotkey_KeyUp, Hotkey_IsActive, FilterAccessibleKeyboardEvents); ResourceLoader resourceLoader = ResourceLoader.GetForViewIndependentUse(); + if (App.GetSettingsWindow() != null) + { + App.GetSettingsWindow().Activated += ShortcutDialog_SettingsWindow_Activated; + } + // We create the Dialog in C# because doing it in XAML is giving WinUI/XAML Island bugs when using dark theme. shortcutDialog = new ContentDialog { @@ -111,8 +116,18 @@ namespace Microsoft.PowerToys.Settings.UI.Controls shortcutDialog.Opened -= ShortcutDialog_Opened; shortcutDialog.Closing -= ShortcutDialog_Closing; + if (App.GetSettingsWindow() != null) + { + App.GetSettingsWindow().Activated -= ShortcutDialog_SettingsWindow_Activated; + } + // Dispose the HotkeySettingsControlHook object to terminate the hook threads when the textbox is unloaded - hook.Dispose(); + if (hook != null) + { + hook.Dispose(); + } + + hook = null; } private void KeyEventHandler(int key, bool matchValue, int matchValueCode) @@ -336,6 +351,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls c.Keys = HotkeySettings.GetKeysList(); shortcutDialog.XamlRoot = this.XamlRoot; + shortcutDialog.RequestedTheme = this.ActualTheme; await shortcutDialog.ShowAsync(); } @@ -363,6 +379,22 @@ namespace Microsoft.PowerToys.Settings.UI.Controls } } + private void ShortcutDialog_SettingsWindow_Activated(object sender, WindowActivatedEventArgs args) + { + args.Handled = true; + if (args.WindowActivationState != WindowActivationState.Deactivated && (hook == null || hook.GetDisposedState() == true)) + { + // If the PT settings window gets focussed/activated again, we enable the keyboard hook to catch the keyboard input. + hook = new HotkeySettingsControlHook(Hotkey_KeyDown, Hotkey_KeyUp, Hotkey_IsActive, FilterAccessibleKeyboardEvents); + } + else if (args.WindowActivationState == WindowActivationState.Deactivated && hook != null && hook.GetDisposedState() == false) + { + // If the PT settings window lost focus/activation, we disable the keyboard hook to allow keyboard input on other windows. + hook.Dispose(); + hook = null; + } + } + private void ShortcutDialog_Closing(ContentDialog sender, ContentDialogClosingEventArgs args) { _isActive = false; @@ -374,7 +406,12 @@ namespace Microsoft.PowerToys.Settings.UI.Controls { if (disposing) { - hook.Dispose(); + if (hook != null) + { + hook.Dispose(); + } + + hook = null; } disposedValue = true; diff --git a/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutDialogContentControl.xaml b/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutDialogContentControl.xaml index 2ea9aa3d94..0429f5dae4 100644 --- a/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutDialogContentControl.xaml +++ b/src/settings-ui/Settings.UI/Controls/ShortcutControl/ShortcutDialogContentControl.xaml @@ -56,39 +56,14 @@ VerticalAlignment="Top" Orientation="Vertical" Spacing="8"> - + - - - - - - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs new file mode 100644 index 0000000000..6692ff6077 --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/AppsListPage.xaml.cs @@ -0,0 +1,36 @@ +// 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. +using System; +using System.Collections.ObjectModel; +using System.Threading; +using global::Windows.System; +using interop; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Microsoft.PowerToys.Settings.UI.Flyout +{ + public sealed partial class AppsListPage : Page + { + private AllAppsViewModel ViewModel { get; set; } + + public AppsListPage() + { + this.InitializeComponent(); + + var settingsUtils = new SettingsUtils(); + ViewModel = new AllAppsViewModel(SettingsRepository.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage); + DataContext = ViewModel; + } + + private void BackButton_Click(object sender, RoutedEventArgs e) + { + Frame.Navigate(typeof(LaunchPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft }); + } + } +} diff --git a/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml new file mode 100644 index 0000000000..05d035275b --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs new file mode 100644 index 0000000000..e16a48461b --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/LaunchPage.xaml.cs @@ -0,0 +1,147 @@ +// 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. +using System; +using System.Collections.ObjectModel; +using System.Threading; +using global::Windows.System; +using interop; +using Microsoft.PowerToys.Settings.UI.Controls; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Microsoft.PowerToys.Settings.UI.Flyout +{ + public sealed partial class LaunchPage : Page + { + private LauncherViewModel ViewModel { get; set; } + + public LaunchPage() + { + this.InitializeComponent(); + var settingsUtils = new SettingsUtils(); + ViewModel = new LauncherViewModel(SettingsRepository.GetInstance(settingsUtils), Views.ShellPage.SendDefaultIPCMessage); + DataContext = ViewModel; + } + + private void ModuleButton_Click(object sender, RoutedEventArgs e) + { + FlyoutMenuButton selectedModuleBtn = sender as FlyoutMenuButton; + bool moduleRun = true; + switch ((string)selectedModuleBtn.Tag) + { + case "ColorPicker": // Launch ColorPicker + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent())) + { + eventHandle.Set(); + } + + break; + case "FancyZones": // Launch FancyZones Editor + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent())) + { + eventHandle.Set(); + } + + break; + + case "Hosts": // Launch Hosts + { + bool launchAdmin = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.LaunchAdministrator; + var actionName = "Launch"; + if (!App.IsElevated && launchAdmin) + { + actionName = "LaunchAdministrator"; + } + + Views.ShellPage.SendDefaultIPCMessage("{\"action\":{\"Hosts\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}"); + } + + break; + + case "RegistryPreview": // Launch Registry Preview + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent())) + { + eventHandle.Set(); + } + + break; + case "MeasureTool": // Launch Screen Ruler + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent())) + { + eventHandle.Set(); + } + + break; + + case "PowerLauncher": // Launch Run + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent())) + { + eventHandle.Set(); + } + + break; + + case "PowerOCR": // Launch Text Extractor + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent())) + { + eventHandle.Set(); + } + + break; + + case "ShortcutGuide": // Launch Shortcut Guide + using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent())) + { + eventHandle.Set(); + } + + break; + + default: + moduleRun = false; + break; + } + + if (moduleRun) + { + PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutModuleRunEvent() { ModuleName = (string)selectedModuleBtn.Tag }); + } + } + + private void SettingsBtn_Click(object sender, RoutedEventArgs e) + { + App.OpenSettingsWindow(null, true); + } + + private async void DocsBtn_Click(object sender, RoutedEventArgs e) + { + await Launcher.LaunchUriAsync(new Uri("https://aka.ms/PowerToysOverview")); + } + + private void AllAppButton_Click(object sender, RoutedEventArgs e) + { + Frame.Navigate(typeof(AppsListPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight }); + } + + private void QuitButton_Click(object sender, RoutedEventArgs e) + { + ViewModel.KillRunner(); + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + Application.Current.Exit(); + }); + } + + private void ReportBugBtn_Click(object sender, RoutedEventArgs e) + { + ViewModel.StartBugReport(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml new file mode 100644 index 0000000000..52b5f542ca --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs new file mode 100644 index 0000000000..fe0aa75f69 --- /dev/null +++ b/src/settings-ui/Settings.UI/Flyout/ShellPage.xaml.cs @@ -0,0 +1,30 @@ +// 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. +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Animation; + +namespace Microsoft.PowerToys.Settings.UI.Flyout +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class ShellPage : Page + { + public ShellPage() + { + this.InitializeComponent(); + } + + internal void SwitchToLaunchPage() + { + ContentFrame.Navigate(typeof(LaunchPage), null, new SuppressNavigationTransitionInfo()); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + SwitchToLaunchPage(); + } + } +} diff --git a/src/settings-ui/Settings.UI/FlyoutWindow.xaml b/src/settings-ui/Settings.UI/FlyoutWindow.xaml new file mode 100644 index 0000000000..8ecffb7013 --- /dev/null +++ b/src/settings-ui/Settings.UI/FlyoutWindow.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs b/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs new file mode 100644 index 0000000000..c816561034 --- /dev/null +++ b/src/settings-ui/Settings.UI/FlyoutWindow.xaml.cs @@ -0,0 +1,95 @@ +// 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. +using Microsoft.PowerToys.Settings.UI.Helpers; +using Microsoft.PowerToys.Settings.UI.Library.Telemetry.Events; +using Microsoft.PowerToys.Settings.UI.ViewModels.Flyout; +using Microsoft.PowerToys.Telemetry; +using Microsoft.UI; +using Microsoft.UI.Windowing; +using Windows.Graphics; +using WinUIEx; + +namespace Microsoft.PowerToys.Settings.UI +{ + /// + /// An empty window that can be used on its own or navigated to within a Frame. + /// + public sealed partial class FlyoutWindow : WindowEx + { + private const int WindowWidth = 386; + private const int WindowHeight = 486; + private const int WindowMargin = 12; + + public FlyoutViewModel ViewModel { get; set; } + + public POINT? FlyoutAppearPosition { get; set; } + + public FlyoutWindow(POINT? initialPosition) + { + this.InitializeComponent(); + this.Activated += FlyoutWindow_Activated; + FlyoutAppearPosition = initialPosition; + ViewModel = new FlyoutViewModel(); + } + + private void FlyoutWindow_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args) + { + PowerToysTelemetry.Log.WriteEvent(new TrayFlyoutActivatedEvent()); + if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.CodeActivated) + { + var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); + WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd); + if (!FlyoutAppearPosition.HasValue) + { + DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest); + double dpiScale = (float)this.GetDpiForWindow() / 96; + double x = displayArea.WorkArea.Width - (dpiScale * (WindowWidth + WindowMargin)); + double y = displayArea.WorkArea.Height - (dpiScale * (WindowHeight + WindowMargin)); + this.MoveAndResize(x, y, WindowWidth, WindowHeight); + } + else + { + DisplayArea displayArea = DisplayArea.GetFromPoint(new PointInt32(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y), DisplayAreaFallback.Nearest); + + // Move the window to the correct screen as a little blob, so we can get the accurate dpi for the screen to calculate the best position to show it. + this.MoveAndResize(FlyoutAppearPosition.Value.X, FlyoutAppearPosition.Value.Y, 1, 1); + double dpiScale = (float)this.GetDpiForWindow() / 96; + + // Position the window so that it's inside the display are closest to the point. + POINT newPosition = new POINT(FlyoutAppearPosition.Value.X - (int)(dpiScale * WindowWidth / 2), FlyoutAppearPosition.Value.Y - (int)(dpiScale * WindowHeight / 2)); + if (newPosition.X < displayArea.WorkArea.X) + { + newPosition.X = displayArea.WorkArea.X; + } + + if (newPosition.Y < displayArea.WorkArea.Y) + { + newPosition.Y = displayArea.WorkArea.Y; + } + + if (newPosition.X + (dpiScale * WindowWidth) > displayArea.WorkArea.X + displayArea.WorkArea.Width) + { + newPosition.X = (int)(displayArea.WorkArea.X + displayArea.WorkArea.Width - (dpiScale * WindowWidth)); + } + + if (newPosition.Y + (dpiScale * WindowHeight) > displayArea.WorkArea.Y + displayArea.WorkArea.Height) + { + newPosition.Y = (int)(displayArea.WorkArea.Y + displayArea.WorkArea.Height - (dpiScale * WindowHeight)); + } + + this.MoveAndResize(newPosition.X, newPosition.Y, WindowWidth, WindowHeight); + } + } + + if (args.WindowActivationState == Microsoft.UI.Xaml.WindowActivationState.Deactivated) + { + if (ViewModel.CanHide) + { + FlyoutShellPage.SwitchToLaunchPage(); + this.Hide(); + } + } + } + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs b/src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs new file mode 100644 index 0000000000..d37cb294a1 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/IRefreshablePage.cs @@ -0,0 +1,12 @@ +// 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. + +// An interface so that pages can define refresh method to refresh their view models. +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + public interface IRefreshablePage + { + void RefreshEnabledState(); + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs index d247c03f1c..4bb0b1a7f9 100644 --- a/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs +++ b/src/settings-ui/Settings.UI/Helpers/NativeMethods.cs @@ -38,6 +38,15 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("user32.dll")] internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + [DllImport("shell32.dll")] + internal static extern IntPtr SHBrowseForFolderW(ref ShellGetFolder.BrowseInformation browseInfo); + + [DllImport("shell32.dll")] + internal static extern int SHGetPathFromIDListW(IntPtr pidl, IntPtr pszPath); + + [DllImport("Comdlg32.dll", CharSet = CharSet.Unicode)] + internal static extern bool GetOpenFileName([In, Out] OpenFileName openFileName); + #pragma warning disable CA1401 // P/Invokes should not be visible [DllImport("user32.dll")] public static extern bool ShowWindow(System.IntPtr hWnd, int nCmdShow); @@ -51,11 +60,15 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers [DllImport("user32.dll")] public static extern bool AllowSetForegroundWindow(int dwProcessId); + [System.Runtime.InteropServices.DllImport("User32.dll")] + public static extern bool SetForegroundWindow(IntPtr handle); + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] public static extern bool FreeLibrary(IntPtr hModule); + #pragma warning restore CA1401 // P/Invokes should not be visible [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] diff --git a/src/settings-ui/Settings.UI/Helpers/OpenFileName.cs b/src/settings-ui/Settings.UI/Helpers/OpenFileName.cs new file mode 100644 index 0000000000..3d7c1d8e90 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/OpenFileName.cs @@ -0,0 +1,36 @@ +// 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. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed.")] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class OpenFileName + { + public int StructSize; + public IntPtr Hwnd = IntPtr.Zero; + public IntPtr Hinst = IntPtr.Zero; + public string Filter; + public string CustFilter; + public int CustFilterMax; + public int FilterIndex; + public string File; + public int MaxFile; + public string FileTitle; + public int MaxFileTitle; + public string InitialDir; + public string Title; + public int Flags; + public short FileOffset; + public short FileExtMax; + public string DefExt; + public int CustData; + public IntPtr Hook = IntPtr.Zero; + public string Template; + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs b/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs new file mode 100644 index 0000000000..32acc06a72 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/ShellGetFolder.cs @@ -0,0 +1,65 @@ +// 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. + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + public class ShellGetFolder + { + public delegate int BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lp, IntPtr wp); + + [StructLayout(LayoutKind.Sequential)] + public struct BrowseInformation + { + public IntPtr HwndOwner; + public IntPtr PidlRoot; + public string PszDisplayName; + public string LpszTitle; + public uint UlFlags; + public BrowseCallbackProc Lpfn; + public IntPtr LParam; + public int IImage; + } + + public static string GetFolderDialog(IntPtr hwndOwner) + { + // windows MAX_PATH with long path enable can be approximated 32k char long + // allocating more than double (unicode) to hold the path + StringBuilder sb = new StringBuilder(65000); + IntPtr bufferAddress = Marshal.AllocHGlobal(65000); + IntPtr pidl = IntPtr.Zero; + BrowseInformation browseInfo; + browseInfo.HwndOwner = hwndOwner; + browseInfo.PidlRoot = IntPtr.Zero; + browseInfo.PszDisplayName = null; + browseInfo.LpszTitle = null; + browseInfo.UlFlags = 0; + browseInfo.Lpfn = null; + browseInfo.LParam = IntPtr.Zero; + browseInfo.IImage = 0; + + try + { + pidl = NativeMethods.SHBrowseForFolderW(ref browseInfo); + if (NativeMethods.SHGetPathFromIDListW(pidl, bufferAddress) == 0) + { + return null; + } + + sb.Append(Marshal.PtrToStringUni(bufferAddress)); + Marshal.FreeHGlobal(bufferAddress); + } + finally + { + // Need to free pidl + Marshal.FreeCoTaskMem(pidl); + } + + return sb.ToString(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/Utils.cs b/src/settings-ui/Settings.UI/Helpers/Utils.cs index d075ccf9c4..7f34815bf8 100644 --- a/src/settings-ui/Settings.UI/Helpers/Utils.cs +++ b/src/settings-ui/Settings.UI/Helpers/Utils.cs @@ -9,7 +9,7 @@ using System.Text.Json; namespace Microsoft.PowerToys.Settings.UI.Helpers { - internal class Utils + internal sealed class Utils { private static string _placementPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\settings-placement.json"); @@ -45,5 +45,13 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers { } } + + public static void BecomeForegroundWindow(IntPtr hWnd) + { + NativeKeyboardHelper.INPUT input = new NativeKeyboardHelper.INPUT { type = NativeKeyboardHelper.INPUTTYPE.INPUT_MOUSE, data = { } }; + NativeKeyboardHelper.INPUT[] inputs = new NativeKeyboardHelper.INPUT[] { input }; + _ = NativeMethods.SendInput(1, inputs, NativeKeyboardHelper.INPUT.Size); + NativeMethods.SetForegroundWindow(hWnd); + } } } diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml b/src/settings-ui/Settings.UI/MainWindow.xaml index 715a25863c..01a08ddf21 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml +++ b/src/settings-ui/Settings.UI/MainWindow.xaml @@ -1,14 +1,17 @@ - - + diff --git a/src/settings-ui/Settings.UI/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/MainWindow.xaml.cs index 8723a01110..0f24c31d61 100644 --- a/src/settings-ui/Settings.UI/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/MainWindow.xaml.cs @@ -6,6 +6,7 @@ using System; using ManagedCommon; using Microsoft.PowerLauncher.Telemetry; using Microsoft.PowerToys.Settings.UI.Helpers; +using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Telemetry; @@ -14,13 +15,14 @@ using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Windows.ApplicationModel.Resources; using Windows.Data.Json; +using WinUIEx; namespace Microsoft.PowerToys.Settings.UI { /// /// An empty window that can be used on its own or navigated to within a Frame. /// - public sealed partial class MainWindow : Window + public sealed partial class MainWindow : WindowEx { public MainWindow(bool isDark, bool createHidden = false) { @@ -73,6 +75,99 @@ namespace Microsoft.PowerToys.Settings.UI App.GetTwoWayIPCManager()?.Send(msg); }); + // open main window + ShellPage.SetOpenMainWindowCallback(() => + { + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + App.OpenSettingsWindow(typeof(GeneralPage))); + }); + + // open main window + ShellPage.SetUpdatingGeneralSettingsCallback((string module, bool isEnabled) => + { + SettingsRepository repository = SettingsRepository.GetInstance(new SettingsUtils()); + GeneralSettings generalSettingsConfig = repository.SettingsConfig; + bool needToUpdate = false; + switch (module) + { + case "AlwaysOnTop": + needToUpdate = generalSettingsConfig.Enabled.AlwaysOnTop != isEnabled; + generalSettingsConfig.Enabled.AlwaysOnTop = isEnabled; break; + case "Awake": + needToUpdate = generalSettingsConfig.Enabled.Awake != isEnabled; + generalSettingsConfig.Enabled.Awake = isEnabled; break; + case "ColorPicker": + needToUpdate = generalSettingsConfig.Enabled.ColorPicker != isEnabled; + generalSettingsConfig.Enabled.ColorPicker = isEnabled; break; + case "FancyZones": + needToUpdate = generalSettingsConfig.Enabled.FancyZones != isEnabled; + generalSettingsConfig.Enabled.FancyZones = isEnabled; break; + case "FileLocksmith": + needToUpdate = generalSettingsConfig.Enabled.FileLocksmith != isEnabled; + generalSettingsConfig.Enabled.FileLocksmith = isEnabled; break; + case "FindMyMouse": + needToUpdate = generalSettingsConfig.Enabled.FindMyMouse != isEnabled; + generalSettingsConfig.Enabled.FindMyMouse = isEnabled; break; + case "Hosts": + needToUpdate = generalSettingsConfig.Enabled.Hosts != isEnabled; + generalSettingsConfig.Enabled.Hosts = isEnabled; break; + case "ImageResizer": + needToUpdate = generalSettingsConfig.Enabled.ImageResizer != isEnabled; + generalSettingsConfig.Enabled.ImageResizer = isEnabled; break; + case "KeyboardManager": + needToUpdate = generalSettingsConfig.Enabled.KeyboardManager != isEnabled; + generalSettingsConfig.Enabled.KeyboardManager = isEnabled; break; + case "MouseHighlighter": + needToUpdate = generalSettingsConfig.Enabled.MouseHighlighter != isEnabled; + generalSettingsConfig.Enabled.MouseHighlighter = isEnabled; break; + case "MouseJump": + needToUpdate = generalSettingsConfig.Enabled.MouseJump != isEnabled; + generalSettingsConfig.Enabled.MouseJump = isEnabled; break; + case "MousePointerCrosshairs": + needToUpdate = generalSettingsConfig.Enabled.MousePointerCrosshairs != isEnabled; + generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break; + case "PastePlain": + needToUpdate = generalSettingsConfig.Enabled.PastePlain != isEnabled; + generalSettingsConfig.Enabled.PastePlain = isEnabled; break; + case "PowerRename": + needToUpdate = generalSettingsConfig.Enabled.PowerRename != isEnabled; + generalSettingsConfig.Enabled.PowerRename = isEnabled; break; + case "PowerLauncher": + needToUpdate = generalSettingsConfig.Enabled.PowerLauncher != isEnabled; + generalSettingsConfig.Enabled.PowerLauncher = isEnabled; break; + case "PowerAccent": + needToUpdate = generalSettingsConfig.Enabled.PowerAccent != isEnabled; + generalSettingsConfig.Enabled.PowerAccent = isEnabled; break; + case "RegistryPreview": + needToUpdate = generalSettingsConfig.Enabled.RegistryPreview != isEnabled; + generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break; + case "MeasureTool": + needToUpdate = generalSettingsConfig.Enabled.MeasureTool != isEnabled; + generalSettingsConfig.Enabled.MeasureTool = isEnabled; break; + case "ShortcutGuide": + needToUpdate = generalSettingsConfig.Enabled.ShortcutGuide != isEnabled; + generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break; + case "PowerOCR": + needToUpdate = generalSettingsConfig.Enabled.PowerOCR != isEnabled; + generalSettingsConfig.Enabled.PowerOCR = isEnabled; break; + case "VideoConference": + needToUpdate = generalSettingsConfig.Enabled.VideoConference != isEnabled; + generalSettingsConfig.Enabled.VideoConference = isEnabled; break; + } + + if (needToUpdate) + { + var outgoing = new OutGoingGeneralSettings(generalSettingsConfig); + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + ShellPage.SendDefaultIPCMessage(outgoing.ToString()); + ShellPage.ShellHandler?.SignalGeneralDataUpdate(); + }); + } + + return needToUpdate; + }); + // open oobe ShellPage.SetOpenOobeCallback(() => { @@ -84,6 +179,40 @@ namespace Microsoft.PowerToys.Settings.UI App.GetOobeWindow().Activate(); }); + // open flyout + ShellPage.SetOpenFlyoutCallback((POINT? p) => + { + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + if (App.GetFlyoutWindow() == null) + { + App.SetFlyoutWindow(new FlyoutWindow(p)); + } + + FlyoutWindow flyout = App.GetFlyoutWindow(); + flyout.FlyoutAppearPosition = p; + flyout.Activate(); + + // https://github.com/microsoft/microsoft-ui-xaml/issues/7595 - Activate doesn't bring window to the foreground + // Need to call SetForegroundWindow to actually gain focus. + Utils.BecomeForegroundWindow(flyout.GetWindowHandle()); + }); + }); + + // disable flyout hiding + ShellPage.SetDisableFlyoutHidingCallback(() => + { + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + if (App.GetFlyoutWindow() == null) + { + App.SetFlyoutWindow(new FlyoutWindow(null)); + } + + App.GetFlyoutWindow().ViewModel.DisableHiding(); + }); + }); + this.InitializeComponent(); // receive IPC Message @@ -140,5 +269,10 @@ namespace Microsoft.PowerToys.Settings.UI NativeMethods.ShowWindow(hWnd, NativeMethods.SW_HIDE); } } + + internal void EnsurePageIsSelected() + { + ShellPage.EnsurePageIsSelected(); + } } } diff --git a/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs b/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs index 148de19256..bdfb3e54f6 100644 --- a/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs +++ b/src/settings-ui/Settings.UI/OOBE/Enums/PowerToysModules.cs @@ -24,6 +24,8 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums VideoConference, MeasureTool, Hosts, + PastePlain, WhatsNew, + RegistryPreview, } } diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml index fe19acd4bc..13c4f7637c 100644 --- a/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeMouseUtils.xaml @@ -35,6 +35,13 @@ x:Uid="Oobe_MouseUtils_MousePointerCrosshairs_Description" Background="Transparent" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs new file mode 100644 index 0000000000..c5171f4159 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewAlternate.xaml.cs @@ -0,0 +1,50 @@ +// 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. + +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.OOBE.Enums; +using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Microsoft.PowerToys.Settings.UI.OOBE.Views +{ + public sealed partial class OobeOverviewAlternate : Page + { + public OobePowerToysModule ViewModel { get; set; } + + public OobeOverviewAlternate() + { + this.InitializeComponent(); + ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]); + DataContext = ViewModel; + + FancyZonesHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.FancyzonesEditorHotkey.Value.GetKeysList(); + RunHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.OpenPowerLauncher.GetKeysList(); + ColorPickerHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.ActivationShortcut.GetKeysList(); + AlwaysOnTopHotkeyControl.Keys = SettingsRepository.GetInstance(new SettingsUtils()).SettingsConfig.Properties.Hotkey.Value.GetKeysList(); + } + + private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + if (OobeShellPage.OpenMainWindowCallback != null) + { + OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage)); + } + + ViewModel.LogOpeningSettingsEvent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + ViewModel.LogOpeningModuleEvent(); + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + ViewModel.LogClosingModuleEvent(); + } + } +} diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml new file mode 100644 index 0000000000..3c2a5abb45 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs new file mode 100644 index 0000000000..40dcb56aa2 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobeOverviewPlaceholder.xaml.cs @@ -0,0 +1,73 @@ +// 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. + +using System.Threading.Tasks; +using AllExperiments; +using Microsoft.PowerToys.Settings.UI.OOBE.Enums; +using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; +using Microsoft.PowerToys.Settings.UI.Services; +using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Navigation; + +namespace Microsoft.PowerToys.Settings.UI.OOBE.Views +{ + public sealed partial class OobeOverviewPlaceholder : Page + { + public OobePowerToysModule ViewModel { get; set; } + + public OobeOverviewPlaceholder() + { + this.InitializeComponent(); + ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]); + DataContext = ViewModel; + } + + private static async Task GetIsExperiment() + { + Experiments landingPageExp = new Experiments(); + var experimentEnabled = await landingPageExp.EnableLandingPageExperimentAsync(); + return experimentEnabled; + } + + private async void Reload() + { + var isExperiment = await GetIsExperiment(); + + if (isExperiment) + { + this.Frame.Navigate(typeof(OobeOverviewAlternate)); + } + else + { + this.Frame.Navigate(typeof(OobeOverview)); + } + } + + private void Page_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + Reload(); + } + + private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + if (OobeShellPage.OpenMainWindowCallback != null) + { + OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage)); + } + + ViewModel.LogOpeningSettingsEvent(); + } + + protected override void OnNavigatedTo(NavigationEventArgs e) + { + ViewModel.LogOpeningModuleEvent(); + } + + protected override void OnNavigatedFrom(NavigationEventArgs e) + { + ViewModel.LogClosingModuleEvent(); + } + } +} diff --git a/src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml b/src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml new file mode 100644 index 0000000000..f85d7928a6 --- /dev/null +++ b/src/settings-ui/Settings.UI/OOBE/Views/OobePastePlain.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs index e3c42b8094..0253b90029 100644 --- a/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/MouseUtilsPage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class MouseUtilsPage : Page + public sealed partial class MouseUtilsPage : Page, IRefreshablePage { private MouseUtilsViewModel ViewModel { get; set; } @@ -36,11 +37,17 @@ namespace Microsoft.PowerToys.Settings.UI.Views SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), + SettingsRepository.GetInstance(settingsUtils), SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; InitializeComponent(); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/PastePlainPage.xaml b/src/settings-ui/Settings.UI/Views/PastePlainPage.xaml new file mode 100644 index 0000000000..216e7fc49f --- /dev/null +++ b/src/settings-ui/Settings.UI/Views/PastePlainPage.xaml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Views/PastePlainPage.xaml.cs b/src/settings-ui/Settings.UI/Views/PastePlainPage.xaml.cs new file mode 100644 index 0000000000..9af9bf9621 --- /dev/null +++ b/src/settings-ui/Settings.UI/Views/PastePlainPage.xaml.cs @@ -0,0 +1,33 @@ +// 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. + +using Microsoft.PowerToys.Settings.UI.Helpers; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.PowerToys.Settings.UI.Views +{ + public sealed partial class PastePlainPage : Page, IRefreshablePage + { + private PastePlainViewModel ViewModel { get; set; } + + public PastePlainPage() + { + var settingsUtils = new SettingsUtils(); + ViewModel = new PastePlainViewModel( + settingsUtils, + SettingsRepository.GetInstance(settingsUtils), + SettingsRepository.GetInstance(settingsUtils), + ShellPage.SendDefaultIPCMessage); + DataContext = ViewModel; + InitializeComponent(); + } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml index c9b640ac30..971bb300d9 100644 --- a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml +++ b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml @@ -76,8 +76,10 @@ + + diff --git a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml.cs b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml.cs index 775bdbae60..91e57b8105 100644 --- a/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml.cs +++ b/src/settings-ui/Settings.UI/Views/PowerAccentPage.xaml.cs @@ -2,13 +2,14 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views { - public sealed partial class PowerAccentPage : Page + public sealed partial class PowerAccentPage : Page, IRefreshablePage { private PowerAccentViewModel ViewModel { get; set; } @@ -19,5 +20,10 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; this.InitializeComponent(); } + + public void RefreshEnabledState() + { + ViewModel.RefreshEnabledState(); + } } } diff --git a/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml b/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml index c2e0686187..cf7e9451e4 100644 --- a/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml +++ b/src/settings-ui/Settings.UI/Views/PowerLauncherPage.xaml @@ -185,6 +185,14 @@ IsOn="{x:Bind ViewModel.TabSelectsContextButtons, Mode=TwoWay}" /> + + + + + + + diff --git a/src/tests/win-app-driver/README.md b/src/tests/win-app-driver/README.md index 0e76f9c717..dfa5d882f5 100644 --- a/src/tests/win-app-driver/README.md +++ b/src/tests/win-app-driver/README.md @@ -56,7 +56,7 @@ If a remote test machine is being used, the IP of the test machine must be used ### Extra tools and information -For tests creation you will need a tool that enables you select any UI element and view the element's accessibility data. For this purpose you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/en/windows/overview) or [Inspect](https://learn.microsoft.com/windows/win32/winauto/inspect-objects?redirectedfrom=MSDN). +For tests creation you will need a tool that enables you select any UI element and view the element's accessibility data. For this purpose you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview) or [Inspect](https://learn.microsoft.com/windows/win32/winauto/inspect-objects?redirectedfrom=MSDN). * `inspect.exe` you can find installed at `C:\Program Files (x86)\Windows Kits\10\bin\\\inspect.exe` * `AccessibilityInsights` you can download [here](https://aka.ms/accessibilityinsights-windows/download) diff --git a/tools/BugReportTool/BugReportTool/EventViewer.cpp b/tools/BugReportTool/BugReportTool/EventViewer.cpp index 81024e2088..f6e9a6baf0 100644 --- a/tools/BugReportTool/BugReportTool/EventViewer.cpp +++ b/tools/BugReportTool/BugReportTool/EventViewer.cpp @@ -57,7 +57,7 @@ namespace if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError())) { dwBufferSize = dwBufferUsed; - pRenderedContent = (LPWSTR)malloc(dwBufferSize); + pRenderedContent = static_cast(malloc(dwBufferSize)); if (pRenderedContent) { EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount); diff --git a/tools/BugReportTool/BugReportTool/InstallationFolder.cpp b/tools/BugReportTool/BugReportTool/InstallationFolder.cpp index 01fcc06d9d..42a4f13653 100644 --- a/tools/BugReportTool/BugReportTool/InstallationFolder.cpp +++ b/tools/BugReportTool/BugReportTool/InstallationFolder.cpp @@ -27,7 +27,7 @@ wstring GetVersion(path filePath) { if (size) { - VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer; + VS_FIXEDFILEINFO* verInfo = static_cast(lpBuffer); if (verInfo->dwSignature == 0xfeef04bd) { version = diff --git a/tools/BugReportTool/BugReportTool/Main.cpp b/tools/BugReportTool/BugReportTool/Main.cpp index c2e1e540e4..e0ac22995b 100644 --- a/tools/BugReportTool/BugReportTool/Main.cpp +++ b/tools/BugReportTool/BugReportTool/Main.cpp @@ -166,7 +166,7 @@ void ReportWindowsVersion(const filesystem::path& tmpDir) { NTSTATUS(WINAPI * RtlGetVersion) (LPOSVERSIONINFOEXW) = nullptr; - *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); + *reinterpret_cast(& RtlGetVersion) = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion"); if (RtlGetVersion) { osInfo.dwOSVersionInfoSize = sizeof(osInfo); @@ -256,7 +256,8 @@ void ReportVCMLogs(const filesystem::path& tmpDir, const filesystem::path& repor void ReportInstallerLogs(const filesystem::path& tmpDir, const filesystem::path& reportDir) { - const char* logFilePrefix = "powertoys-bootstrapper-msi-"; + const char* bootstrapperLogFilePrefix = "powertoys-bootstrapper-msi-"; + const char* PTLogFilePrefix = "PowerToysMSIInstaller_"; for (auto& entry : directory_iterator{ tmpDir }) { @@ -267,7 +268,7 @@ void ReportInstallerLogs(const filesystem::path& tmpDir, const filesystem::path& } const auto fileName = entry.path().filename().string(); - if (!fileName.starts_with(logFilePrefix)) + if (!fileName.starts_with(bootstrapperLogFilePrefix) && !fileName.starts_with(PTLogFilePrefix)) { continue; } diff --git a/tools/BugReportTool/BugReportTool/ProcessesList.cpp b/tools/BugReportTool/BugReportTool/ProcessesList.cpp index c6e0fce3d6..9e719a170d 100644 --- a/tools/BugReportTool/BugReportTool/ProcessesList.cpp +++ b/tools/BugReportTool/BugReportTool/ProcessesList.cpp @@ -15,6 +15,7 @@ std::vector processes = L"PowerToys.PowerAccent.exe", L"PowerToys.PowerLauncher.exe", L"PowerToys.PowerOCR.exe", + L"PowerToys.MouseJumpUI.exe", L"PowerToys.MeasureToolUI.exe", L"PowerToys.ShortcutGuide.exe", L"PowerToys.PowerRename.exe", @@ -31,5 +32,6 @@ std::vector processes = L"PowerToys.PdfThumbnailProvider.exe", L"PowerToys.StlThumbnailProvider.exe", L"PowerToys.SvgPreviewHandler.exe", - L"PowerToys.SvgThumbnailProvider.exe" + L"PowerToys.SvgThumbnailProvider.exe", + L"PowerToys.RegistryPreview.exe" }; diff --git a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp index 693257d9e8..1085d913bc 100644 --- a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp +++ b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp @@ -126,7 +126,7 @@ namespace stream << achValue; } - stream << " > " << (LPCTSTR)value << "\n"; + stream << " > " << reinterpret_cast(value) << "\n"; } else { diff --git a/tools/BugReportTool/BugReportTool/ReportGPOValues.cpp b/tools/BugReportTool/BugReportTool/ReportGPOValues.cpp index dcda0253a8..33526876b9 100644 --- a/tools/BugReportTool/BugReportTool/ReportGPOValues.cpp +++ b/tools/BugReportTool/BugReportTool/ReportGPOValues.cpp @@ -47,7 +47,9 @@ void ReportGPOValues(const std::filesystem::path& tmpDir) report << "getConfiguredKeyboardManagerEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredKeyboardManagerEnabledValue()) << std::endl; report << "getConfiguredFindMyMouseEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredFindMyMouseEnabledValue()) << std::endl; report << "getConfiguredMouseHighlighterEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMouseHighlighterEnabledValue()) << std::endl; + report << "getConfiguredMouseJumpEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMouseJumpEnabledValue()) << std::endl; report << "getConfiguredMousePointerCrosshairsEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMousePointerCrosshairsEnabledValue()) << std::endl; + report << "getConfiguredPastePlainEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPastePlainEnabledValue()) << std::endl; report << "getConfiguredPowerRenameEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPowerRenameEnabledValue()) << std::endl; report << "getConfiguredPowerLauncherEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredPowerLauncherEnabledValue()) << std::endl; report << "getConfiguredQuickAccentEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredQuickAccentEnabledValue()) << std::endl; @@ -55,5 +57,9 @@ void ReportGPOValues(const std::filesystem::path& tmpDir) report << "getConfiguredShortcutGuideEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredShortcutGuideEnabledValue()) << std::endl; report << "getConfiguredTextExtractorEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredTextExtractorEnabledValue()) << std::endl; report << "getConfiguredVideoConferenceMuteEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue()) << std::endl; + report << "getDisableAutomaticUpdateDownloadValue: " << gpo_rule_configured_to_string(powertoys_gpo::getDisableAutomaticUpdateDownloadValue()) << std::endl; + report << "getSuspendNewUpdateToastValue: " << gpo_rule_configured_to_string(powertoys_gpo::getSuspendNewUpdateToastValue()) << std::endl; + report << "getDisablePeriodicUpdateCheckValue: " << gpo_rule_configured_to_string(powertoys_gpo::getDisablePeriodicUpdateCheckValue()) << std::endl; + report << "getAllowExperimentationValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowExperimentationValue()) << std::endl; } diff --git a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp index 96e14ac3f6..888f26e48e 100644 --- a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp +++ b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp @@ -15,7 +15,7 @@ namespace }; auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM prm) -> BOOL { - std::wostream& os = *((capture*)prm)->os; + std::wostream& os = *(reinterpret_cast(prm))->os; MONITORINFOEX mi; mi.cbSize = sizeof(mi); @@ -48,7 +48,7 @@ namespace capture c; c.os = &os; - if (EnumDisplayMonitors(nullptr, nullptr, callback, (LPARAM)&c)) + if (EnumDisplayMonitors(nullptr, nullptr, callback, reinterpret_cast(& c))) { os << "EnumDisplayMonitors OK\n"; } diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/App.xaml.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/App.xaml.cs index 7074a87893..72ca27d12d 100644 --- a/tools/FancyZone_HitTest/FancyZone_HitTest/App.xaml.cs +++ b/tools/FancyZone_HitTest/FancyZone_HitTest/App.xaml.cs @@ -1,4 +1,8 @@ -using System; +// 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. + +using System; using System.Collections.Generic; using System.Configuration; using System.Data; diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/AssemblyInfo.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/AssemblyInfo.cs index 8b5504ecfb..f0c9de69e8 100644 --- a/tools/FancyZone_HitTest/FancyZone_HitTest/AssemblyInfo.cs +++ b/tools/FancyZone_HitTest/FancyZone_HitTest/AssemblyInfo.cs @@ -1,10 +1,9 @@ +// 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. + using System.Windows; [assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] + ResourceDictionaryLocation.None, + ResourceDictionaryLocation.SourceAssembly)] diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/MainWindow.xaml.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/MainWindow.xaml.cs index 14b3d98042..65ed5140ad 100644 --- a/tools/FancyZone_HitTest/FancyZone_HitTest/MainWindow.xaml.cs +++ b/tools/FancyZone_HitTest/FancyZone_HitTest/MainWindow.xaml.cs @@ -1,4 +1,8 @@ -using System; +// 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. + +using System; using System.Collections; using System.Linq; using System.Windows; @@ -18,8 +22,8 @@ namespace FancyZone_HitTest InitializeComponent(); } - static ArrayList _hitResultsList = new ArrayList(); - static ArrayList _visualCalculationList = new ArrayList(); + private static ArrayList _hitResultsList = new ArrayList(); + private static ArrayList _visualCalculationList = new ArrayList(); private void Grid_MouseMove(object sender, MouseEventArgs e) { @@ -31,9 +35,7 @@ namespace FancyZone_HitTest _visualCalculationList.Clear(); // Set up a callback to receive the hit test result enumeration. - VisualTreeHelper.HitTest(hitTestGrid, null, - new HitTestResultCallback(MyHitTestResult), - new PointHitTestParameters(gridMouseLocation)); + VisualTreeHelper.HitTest(hitTestGrid, null, new HitTestResultCallback(MyHitTestResult), new PointHitTestParameters(gridMouseLocation)); // Perform actions on the hit test results list. if (_hitResultsList.Count > 0) @@ -73,18 +75,16 @@ namespace FancyZone_HitTest item.RelativeMouseLocation, // 3 item.MouseDistanceFromCenter, // 4 item.Area, // 5 - item.Area / item.MouseDistanceFromCenter, //6 + item.Area / item.MouseDistanceFromCenter, // 6 item.Name, // 7 - item.DistanceFromEdge, //8 - item.DistanceFromEdgePercentage // 9 - ); + item.DistanceFromEdge, // 8 + item.DistanceFromEdgePercentage); // 9 itemsHit.Text += Environment.NewLine; } - if (reorderedVisualData.Count() > 0) { - var rect = (hitTestGrid.FindName(reorderedVisualData.First().Name) as Rectangle); + var rect = hitTestGrid.FindName(reorderedVisualData.First().Name) as Rectangle; rect.Opacity = .75; rect.Stroke = Brushes.Black; rect.StrokeThickness = 5; @@ -92,7 +92,7 @@ namespace FancyZone_HitTest } else { - itemsHit.Text = ""; + itemsHit.Text = string.Empty; } } diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/Utilities.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/Utilities.cs index 0e3435842c..5c12984b2b 100644 --- a/tools/FancyZone_HitTest/FancyZone_HitTest/Utilities.cs +++ b/tools/FancyZone_HitTest/FancyZone_HitTest/Utilities.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +// 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. + using System.Windows; using System.Windows.Media; diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/VisualData.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/VisualData.cs index 516d7ce9e9..da05363085 100644 --- a/tools/FancyZone_HitTest/FancyZone_HitTest/VisualData.cs +++ b/tools/FancyZone_HitTest/FancyZone_HitTest/VisualData.cs @@ -1,4 +1,8 @@ -using System; +// 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. + +using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; @@ -8,14 +12,21 @@ namespace FancyZone_HitTest { public class VisualData { - public Point RelativeMouseLocation; - public Point CenterMass; - public Point TopLeft; - public double MouseDistanceFromCenter; - public int Area; - public string Name; - public double DistanceFromEdge; - public double DistanceFromEdgePercentage; + public Point RelativeMouseLocation { get; set; } + + public Point CenterMass { get; set; } + + public Point TopLeft { get; set; } + + public double MouseDistanceFromCenter { get; set; } + + public int Area { get; set; } + + public string Name { get; set; } + + public double DistanceFromEdge { get; set; } + + public double DistanceFromEdgePercentage { get; set; } public VisualData(Shape item, MouseEventArgs e, Visual root) { @@ -36,7 +47,7 @@ namespace FancyZone_HitTest var horDist = (RelativeMouseLocation.X < CenterMass.X) ? RelativeMouseLocation.X : width - mouseX; var vertDist = (RelativeMouseLocation.Y < CenterMass.Y) ? RelativeMouseLocation.Y : height - mouseY; - var isHorCalc = (horDist < vertDist); + var isHorCalc = horDist < vertDist; DistanceFromEdge = Math.Floor(isHorCalc ? horDist : vertDist); if (isHorCalc) diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/VisualDataComparer.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/VisualDataComparer.cs deleted file mode 100644 index f028d4b603..0000000000 --- a/tools/FancyZone_HitTest/FancyZone_HitTest/VisualDataComparer.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; - -namespace FancyZone_HitTest -{ - public class VisualDataComparer : IComparer - { - int IComparer.Compare(VisualData x, VisualData y) - { - // has quirks but workable - if (x.DistanceFromEdge == y.DistanceFromEdge) - { - return y.Area.CompareTo(x.Area); - } - else - { - return x.DistanceFromEdge.CompareTo(y.DistanceFromEdge); - } - - // entire screen won't work - //if (x.MouseDistanceFromCenter == y.MouseDistanceFromCenter) - //{ - // return y.Area.CompareTo(x.Area); - //} - //else - //{ - // return x.MouseDistanceFromCenter.CompareTo(y.MouseDistanceFromCenter); - //} - - //if (x.DistanceFromEdgePercentage == y.DistanceFromEdgePercentage) - //{ - // return y.Area.CompareTo(x.Area); - //} - //else - //{ - // return x.DistanceFromEdgePercentage.CompareTo(y.DistanceFromEdgePercentage); - //} - } - } -} diff --git a/tools/FancyZone_HitTest/FancyZone_HitTest/VisualDataComparer`1.cs b/tools/FancyZone_HitTest/FancyZone_HitTest/VisualDataComparer`1.cs new file mode 100644 index 0000000000..9a331baa62 --- /dev/null +++ b/tools/FancyZone_HitTest/FancyZone_HitTest/VisualDataComparer`1.cs @@ -0,0 +1,44 @@ +// 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. + +using System.Collections.Generic; + +namespace FancyZone_HitTest +{ + public class VisualDataComparer : IComparer + { + int IComparer.Compare(VisualData x, VisualData y) + { + // has quirks but workable + if (x.DistanceFromEdge == y.DistanceFromEdge) + { + return y.Area.CompareTo(x.Area); + } + else + { + return x.DistanceFromEdge.CompareTo(y.DistanceFromEdge); + } + + // entire screen won't work + /* + if (x.MouseDistanceFromCenter == y.MouseDistanceFromCenter) + { + return y.Area.CompareTo(x.Area); + } + else + { + return x.MouseDistanceFromCenter.CompareTo(y.MouseDistanceFromCenter); + } + + if (x.DistanceFromEdgePercentage == y.DistanceFromEdgePercentage) + { + return y.Area.CompareTo(x.Area); + } + else + { + return x.DistanceFromEdgePercentage.CompareTo(y.DistanceFromEdgePercentage); + }*/ + } + } +} diff --git a/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.cpp b/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.cpp index dec87a7907..0f65e17c22 100644 --- a/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.cpp +++ b/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.cpp @@ -68,7 +68,7 @@ int GetHighlightedZoneIdx(const std::vector& zones, const POINT& cursorPos { if (cursorPosition.x >= zones[i].left && cursorPosition.x < zones[i].right) { - return i; + return static_cast(i); } } return -1; @@ -361,7 +361,7 @@ void DrawZone(HDC hdc, const ColorSetting& colorSetting, const RECT& rect, size_ DrawIndex(hdc, rect, index); } -inline BYTE OpacitySettingToAlpha(int opacity) +constexpr inline BYTE OpacitySettingToAlpha(int opacity) { return static_cast(opacity * 2.55); } diff --git a/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.vcxproj b/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.vcxproj index 5fd9dd3166..51ae22a786 100644 --- a/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.vcxproj +++ b/tools/FancyZones_DrawLayoutTest/FancyZones_DrawLayoutTest.vcxproj @@ -107,6 +107,7 @@ true _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true + NotUsing Windows @@ -121,6 +122,7 @@ true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true + NotUsing Windows diff --git a/tools/FancyZones_zonable_tester/FancyZones_zonable_tester.vcxproj b/tools/FancyZones_zonable_tester/FancyZones_zonable_tester.vcxproj index 737c70d64f..06cc9201af 100644 --- a/tools/FancyZones_zonable_tester/FancyZones_zonable_tester.vcxproj +++ b/tools/FancyZones_zonable_tester/FancyZones_zonable_tester.vcxproj @@ -80,6 +80,7 @@ true true stdcpplatest + NotUsing Console @@ -110,6 +111,7 @@ true true stdcpplatest + NotUsing Console diff --git a/tools/FancyZones_zonable_tester/main.cpp b/tools/FancyZones_zonable_tester/main.cpp index f39eb214c7..534b84efee 100644 --- a/tools/FancyZones_zonable_tester/main.cpp +++ b/tools/FancyZones_zonable_tester/main.cpp @@ -11,7 +11,7 @@ std::wstring get_process_path(DWORD pid) noexcept { name.resize(MAX_PATH); DWORD name_length = static_cast(name.length()); - if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0) + if (QueryFullProcessImageNameW(process, 0, static_cast(name.data()), &name_length) == 0) { name_length = 0; } @@ -185,8 +185,8 @@ bool test_window(HWND window) auto style = GetWindowLongPtr(window, GWL_STYLE); auto exStyle = GetWindowLongPtr(window, GWL_EXSTYLE); - std::cout << "style: 0x" << std::hex << style << ": " << window_styles(style) << "\n"; - std::cout << "exStyle: 0x" << std::hex << exStyle << ": " << window_exstyles(exStyle) << " \n"; + std::cout << "style: 0x" << std::hex << style << ": " << window_styles(static_cast(style)) << "\n"; + std::cout << "exStyle: 0x" << std::hex << exStyle << ": " << window_exstyles(static_cast(exStyle)) << " \n"; std::array class_name; GetClassNameA(window, class_name.data(), static_cast(class_name.size())); std::cout << "Window class: '" << class_name.data() << "' equals:\n"; diff --git a/tools/MonitorReportTool/MonitorReportTool.cpp b/tools/MonitorReportTool/MonitorReportTool.cpp index 747ea91e1a..289ebf5561 100644 --- a/tools/MonitorReportTool/MonitorReportTool.cpp +++ b/tools/MonitorReportTool/MonitorReportTool.cpp @@ -221,7 +221,7 @@ void LogWMI() // on a particular host computer. IWbemLocator* pLocator = 0; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator); + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(&pLocator)); if (FAILED(hres)) { Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres); @@ -346,7 +346,7 @@ void LogWMICIMV2() // on a particular host computer. IWbemLocator* pLocator = 0; - hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator); + hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(&pLocator)); if (FAILED(hres)) { Logger::log(L"Failed to create IWbemLocator object. Error code = ", hres); diff --git a/tools/StylesReportTool/StylesReportTool.cpp b/tools/StylesReportTool/StylesReportTool.cpp index 40b1f23f8e..4b84e08dac 100644 --- a/tools/StylesReportTool/StylesReportTool.cpp +++ b/tools/StylesReportTool/StylesReportTool.cpp @@ -49,7 +49,7 @@ inline std::wstring get_process_path(DWORD pid) noexcept { name.resize(MAX_PATH); DWORD name_length = static_cast(name.length()); - if (QueryFullProcessImageNameW(process, 0, (LPWSTR)name.data(), &name_length) == 0) + if (QueryFullProcessImageNameW(process, 0, static_cast(name.data()), &name_length) == 0) { name_length = 0; } @@ -410,7 +410,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) LPCWSTR text = L"Please select the target window (using a mouse or Alt+Tab), \r\nand press Ctrl+Alt+S to capture its styles. \r\nYou can find the output file \"window_styles.txt\" on your desktop."; RECT rc{0,50,600,200}; - DrawText(hdc, text, (int)wcslen(text), &rc, DT_CENTER | DT_WORDBREAK); + DrawText(hdc, text, static_cast(wcslen(text)), &rc, DT_CENTER | DT_WORDBREAK); EndPaint(hWnd, &ps); } diff --git a/tools/WebcamReportTool/DirectShowUtils.h b/tools/WebcamReportTool/DirectShowUtils.h index 71a091648d..00e3a6e7c9 100644 --- a/tools/WebcamReportTool/DirectShowUtils.h +++ b/tools/WebcamReportTool/DirectShowUtils.h @@ -6,10 +6,13 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// disable warning 26492 - Don't use const_cast to cast away const on winrt +// disable warning 26493 - Don't use C-style casts +// Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable: 26471) +#pragma warning(disable : 26471 26492 26493 26497) #include -#pragma warning(push) +#pragma warning(pop) #include diff --git a/tools/WebcamReportTool/main.cpp b/tools/WebcamReportTool/main.cpp index 3a7501ce23..e0f0a625b4 100644 --- a/tools/WebcamReportTool/main.cpp +++ b/tools/WebcamReportTool/main.cpp @@ -6,10 +6,13 @@ #include // disable warning 26471 - Don't use reinterpret_cast. A cast from void* can use static_cast +// disable warning 26492 - Don't use const_cast to cast away const +// disable warning 26493 - Don't use C-style casts +// Disable 26497 for winrt - This function function-name could be marked constexpr if compile-time evaluation is desired. #pragma warning(push) -#pragma warning(disable: 26471) +#pragma warning(disable : 26471 26492 26493 26497) #include -#pragma warning(push) +#pragma warning(pop) #include @@ -176,7 +179,7 @@ void ReportAllWebcams() std::string friendlyName; for (wchar_t c : wideFriendlyName) { - friendlyName += (char)c; + friendlyName += static_cast(c); } log() << "Webcam " << friendlyName << '\n';