[PT Run][New Plugin] Time zone plugin (#11431)

* Initial commit - simple idea for a time zone plugin

* Translations, better search results, copy to clipboard, cleanup

* fix typo

* Add shortcut search and prepare JSON for later usage

* Fix typo

* Use timezone Data only from JSON

* Exclude json file from spell checker

* fix wrong dst

* Improved results (title, subtitle, tooltip) and fix namespace/class problem

* Always show full offset (-##:## and +##:##)

* Add and show timezone names (first pass)

* Fix typos

* fix build

* JSON: fix wrong minus sign and put extra country info the end

* Improved Subtitle for many matched countries and allow full offset search (+ and -)

* Allow more than one names for time zones and remove leftover

* Add military time zone names, and fix name result

* Only use one JSON entry for one time zone

* Use TimeSpan for offset, use build-in calculation for time in time zone

* add descriptions for JSON schema

* Fix typos

* Split out names in separate properties

* Add many time names, time zone names and shortcuts

* Add additional options and most code documentation

* Fix unreadable TimeSpans in JSON and rename helper class

* Fix not allowed commas in JSON file

* Cut to long time and time zone names in title

* Fix missing results for names and offsets

* Better result and show only one result when offset are identical (respect daylight saving time)

* Show generic name fot time zones without names

* Typo fixes

* Fix not working serach by shortcuts

* Fix german resx file -> english resx file

* Translate all names and countires

* Fix not working context menu

* Typo fixes, fix wrong shortcut in names, comments, few better variable names

* New symbols - thx to niels9001

* Search by shortcuts for time names

* update schema

* Add more time zone names and shortcuts (second pass), make spell checker happy

* Reduce matching checks

* Show shortcuts in tool-tips, avoid string converting

* Show only names that match the query

* Make all translatable (Part 1)

* Make all translatable (part 2 of 2)

* XML Doc

* Fix plugin name (type)

* Fix Typos

* Add TimeZone Plugint to WXS

* Add TimeZone plugin to sign pipeline

* Add Documentation

* Remove double spell entries

* Remove TODO leftovers

* Fix for results with no countries

* Fix typos

* fix typos

* Fix broken siolution after rebase

* Update target framework to make build happy

* fix wrong guid count in WXS

* fix wrong output folder (setup wasn’t found files)

* Address feedback from @jsoref - fix spell check

* typo fix - one leftover in expect.txt

* Switch to .NET6 and update dokumentation

* Address feedbacks, and fix search bug

* fix installer build error

* fix spellchecker

* Address feedback from @htcfreek

Co-authored-by: Sekan, Tobias <tobias.sekan@axp-consulting.de>
This commit is contained in:
Tobias Sekan
2022-02-23 14:26:48 +00:00
committed by GitHub
parent 8edfb8fe80
commit 84e142631e
23 changed files with 12484 additions and 11 deletions

View File

@@ -49,3 +49,5 @@ ignore$
^\.github/
^\.github/actions/spell-check/
^\.gitmodules$
(?:^|/)WindowsSettings\.json$
(?:^|/)timezones\.json$

View File

@@ -34,6 +34,8 @@ AFX
AGGREGATABLE
AHybrid
Aissue
Akrotiri
Aktobe
ALarger
alekhyareddy
alignas
@@ -50,6 +52,7 @@ Amicrosoft
AModifier
AMPROPERTY
AMPROPSETID
Andreanof
anges
angularsen
ansicolor
@@ -83,6 +86,7 @@ appx
appxmanifest
APSTUDIO
AQS
Aqtobe
arcosh
ARemapped
argb
@@ -98,6 +102,7 @@ ARRAYSIZE
arsinh
artanh
Artboard
Artsakh
asdf
AShortcut
ASingle
@@ -106,6 +111,7 @@ aspnet
ASSOCCHANGED
ASYNCWINDOWPLACEMENT
ASYNCWINDOWPOS
Atikokan
atl
atlbase
atlcom
@@ -115,6 +121,7 @@ atlstr
atop
Attribs
attrs
Atyrau
aumid
Aut
Authenticode
@@ -127,6 +134,7 @@ AUTOMATIONPROPERTIES
Autorun
AUTOUPDATE
AValid
Avanc
Awaitable
awakeness
awakeversion
@@ -136,13 +144,17 @@ AZCLI
azurecr
backend
backtracer
BADD
BAEC
BAF
bak
Bashkortostan
Bayan
bbwe
bck
Bcl
BEEE
Belarus
betadele
betsegaw
BFB
@@ -201,8 +213,11 @@ BUILDARCH
buildcommand
buildtools
buildtransitive
Burkina
Buryatia
BValue
bytearray
Caiguna
CALG
callbackptr
cameligo
@@ -235,12 +250,14 @@ ChaseKnowlden
chdir
CHILDACTIVATE
CHILDWINDOW
Choibalsan
chrdavis
chromaticities
chrono
Chrzan
chrzan
CHT
Chukotka
Chuuk
cielab
ciexyz
CImage
@@ -258,11 +275,11 @@ CLIENTEDGE
CLIENTPULL
clientside
CLIPCHILDREN
Clipperton
CLIPSIBLINGS
cljs
clrcall
Cls
cls
CLSCTX
clsid
Clusion
@@ -278,6 +295,7 @@ CMONITORS
cmp
cmyk
cnt
Cocklebiddy
coclass
codebase
codecvt
@@ -302,6 +320,7 @@ comhost
cominterop
commandline
commctrl
Comoros
companding
Compat
COMPOSITIONFULL
@@ -369,6 +388,7 @@ CTRLALTDEL
Ctrls
Ctx
CUI
Cunha
currentculture
CURSORINFO
cursorpos
@@ -388,6 +408,7 @@ czf
cziplib
Dac
dacl
Danmarkshavn
DARKPURPLE
DARKTEAL
DARKYELLOW
@@ -572,9 +593,11 @@ ESettings
esize
esrp
estdir
Eswatini
etcore
etl
etw
Eucla
EUQ
eventlog
everytime
@@ -609,6 +632,7 @@ fancyzones
FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
Farbraum
Faroe
FARPROC
FBF
FCCD
@@ -638,13 +662,13 @@ findfast
findstr
Firefox
FIXEDFILEINFO
FIXME
FLASHZONES
FLASHZONESONQUICKSWITCH
flt
flyout
fmtlib
FOF
fof
FOFX
FOLDERID
folderpath
@@ -663,6 +687,7 @@ fstream
FTYPE
func
Functiondiscoverykeys
Futuna
fwlink
fwrite
fxcop
@@ -732,6 +757,7 @@ hdrop
HEB
Heiko
helptext
Heure
HEVC
hfile
hglobal
@@ -774,6 +800,7 @@ hotkeycontrol
hotkeys
hotlight
hotspot
Hovd
HPAINTBUFFER
HRAWINPUT
hread
@@ -1002,6 +1029,7 @@ ith
IThrottled
ithumbnail
ITrigger
Ittoqqortoormiit
IUI
IUnknown
IUri
@@ -1060,14 +1088,23 @@ keystokes
Keystool
Keytool
keyup
Khakassia
Khanty
Khovd
KILLFOCUS
Kitts
Knowlden
Knownfolders
kotlin
Krai
KSPROPERTY
ktm
kts
Kwango
Kwilu
Kybd
Kyrgyzstan
Kyzylorda
LAlt
Lambson
lamotile
@@ -1075,7 +1112,6 @@ langword
Lastdevice
LASTEXITCODE
Laute
laute
laviusmotileng
LAYOUTRTL
LBUTTON
@@ -1091,14 +1127,17 @@ Ldone
ldx
LEFTSCROLLBAR
lego
lemy
len
LEQ
LError
Lessthan
LEVELID
LExit
lgii
lhs
lhwnd
lia
LIBID
LIGHTORANGE
LIGHTTURQUOISE
@@ -1168,10 +1207,14 @@ lstrcmpi
lstrlen
LTRB
LTRREADING
Luhansk
LWA
lwin
LZero
lzw
Maarten
Macquarie
Magadan
MAINICON
Mainwindow
majortype
@@ -1182,13 +1225,18 @@ MAKEINTRESOURCEW
MAKELPARAM
makepri
malloc
Mangere
Mangystau
manifestdependency
Mansi
MAPPEDTOSAMEKEY
MAPTOSAMESHORTCUT
MAPVK
Markdig
Marquesas
martinchrzan
martinmoene
Mato
MAXIMIZEBOX
MAXSHORTCUTSIZE
maxversiontested
@@ -1197,6 +1245,8 @@ MBUTTON
MBUTTONDBLCLK
MBUTTONDOWN
MBUTTONUP
MCDT
MCST
MDICHILD
MDL
mdpreviewhandler
@@ -1236,6 +1286,7 @@ miniz
minlevel
Miracast
mirophone
Mishkeegogamang
mjpg
mkdir
mlcfg
@@ -1249,6 +1300,8 @@ MODECHANGE
moderncop
modernwpf
modulekey
Moldova
Mongala
MONITORINFO
MONITORINFOEX
MONITORINFOEXW
@@ -1302,23 +1355,27 @@ MULTIPLEUSE
Multiselect
multiset
multizone
Mundrabilla
mutex
mutexes
muxc
mvvm
myfile
MYICON
MYTZ
NAMECHANGE
nameof
namespace
namings
NATIVEFNTCTL
Navassa
NCACTIVATE
ncc
NCCALCSIZE
NCCREATE
NCDESTROY
NCHITTEST
ncipe
NCLBUTTONDBLCLK
NCLBUTTONDOWN
NCLBUTTONUP
@@ -1333,6 +1390,7 @@ NCRBUTTONDBLCLK
NCRBUTTONDOWN
NCRBUTTONUP
NDEBUG
Ndombe
ndp
NEEDDISPATCH
neq
@@ -1353,6 +1411,7 @@ newitem
newpath
newrow
Newtonsoft
nia
niels
nielslaute
NIF
@@ -1389,6 +1448,7 @@ NOREPEAT
NOREPOSITION
NORMALDISPLAY
NORMALUSER
Noronha
NOSEARCH
NOSENDCHANGING
NOSIZE
@@ -1417,6 +1477,8 @@ nullptr
numberbox
NUMLOCK
NUMPAD
Nunavut
Nusa
Nvidia
nwc
NWSE
@@ -1469,6 +1531,7 @@ Overridable
Oversampling
OWNDC
PACL
pagos
PAINTSTRUCT
PAIT
PALEBLUE
@@ -1491,7 +1554,6 @@ pch
PCIDLIST
PCWSTR
Pdb
pdb
pdbonly
pdfpreviewhandler
pdo
@@ -1522,6 +1584,7 @@ pinfo
pinvoke
Pipelinhttps
pipename
Pitcairn
PKBDLLHOOKSTRUCT
PKEY
plib
@@ -1533,6 +1596,7 @@ plugin
pluginsmodel
plx
PMSIHANDLE
Pohnpei
policheck
popd
popup
@@ -1579,6 +1643,7 @@ PREVIOUSVERSIONSINSTALLED
prevpane
prgms
pri
Primorsky
PRINTCLIENT
printf
prm
@@ -1633,6 +1698,7 @@ qit
QITAB
QITABENT
qps
quateur
Queryable
QUERYENDSESSION
QUERYOPEN
@@ -1746,6 +1812,7 @@ robmensching
robocopy
Roboto
roslyn
Rothera
royvou
Rpc
RRF
@@ -1873,6 +1940,7 @@ signtool
SINGLEKEY
singlekeyremapcontrol
singletones
Sint
sipolicy
SIZEBOX
sizeg
@@ -1918,6 +1986,7 @@ sqlite
SRCCOPY
Srch
sre
Srednekolymsk
sregex
SResize
SRGB
@@ -1987,7 +2056,9 @@ subkey
SUBLANG
subquery
substr
Sul
Superbar
Suri
sut
SVE
SVGIn
@@ -2024,7 +2095,7 @@ SYSTEMAPPS
SYSTEMTIME
systemverilog
Tadele
tadele
Tajikistan
talynone
TApp
TApplication
@@ -2051,6 +2122,7 @@ telem
tellg
Templated
templatenamespace
Tenggara
testcase
testhost
testprocess
@@ -2069,6 +2141,8 @@ timediff
Timeline
timeunion
timeutil
timezone
timezones
Titlecase
TLayout
tlb
@@ -2081,7 +2155,6 @@ Toolchain
toolkitcontrols
toolkitconverters
Toolset
toolset
toolstrip
toolwindow
TOPDOWNDIB
@@ -2095,14 +2168,18 @@ tracelogging
trackpad
traies
transcoded
Transnistria
transparrent
TRAYMOUSEMESSAGE
triaging
TRK
trl
trunc
Tshuapa
tspan
TStr
Tuva
TValue
TYMED
typedef
TYPEKEY
@@ -2118,6 +2195,7 @@ UAC
UAL
uap
udit
Udmurtia
Udp
uefi
UHash
@@ -2128,6 +2206,7 @@ uintptr
UIPI
UIs
UITo
Ulaanbaatar
ULARGE
ULLONG
ulong
@@ -2169,6 +2248,7 @@ Uptool
upvote
uri
URLs
Urville
Usb
USEDEFAULT
USEFILEATTRIBUTES
@@ -2184,6 +2264,7 @@ Utc
utf
utils
uuidof
Uvs
uwp
UWPUI
uxtheme
@@ -2402,6 +2483,7 @@ XResource
xsi
XStr
XVIRTUALSCREEN
Yamalia
YIncrement
yinwang
YOffset
@@ -2412,6 +2494,7 @@ YUYV
YVIRTUALSCREEN
YVU
YVYU
Zabaykalsky
ZEROINIT
ZIndex
zonable
@@ -2420,4 +2503,5 @@ ZONEHIGHLIGHTCOLOR
zoneset
ZONESETCHANGE
Zoneszonabletester
zzz
Zonev
zzz

View File

@@ -94,6 +94,7 @@
"modules\\launcher\\Plugins\\VSCodeWorkspaces\\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll",
"modules\\launcher\\Plugins\\Service\\Microsoft.PowerToys.Run.Plugin.Service.dll",
"modules\\launcher\\Plugins\\System\\Microsoft.PowerToys.Run.Plugin.System.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",

View File

@@ -397,6 +397,8 @@ 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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -1080,6 +1082,15 @@ 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
{04B193D7-3E21-46B8-A958-89B63A8A69DE}.Release|x86.ActiveCfg = Release|Any CPU
{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}.Debug|x86.Build.0 = Debug|x64
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1211,6 +1222,7 @@ 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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -0,0 +1,150 @@
# 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 |
| `GlobalSuppressions.cs` | Code suppressions (no real file, linked via *.csproj) |
| `Main.cs` | Main class, the only place that implement the WOX interfaces |
| `plugin.json` | All meta-data for this plugin |
| `StyleCop.json` | Code style (no real file, linked via *.csproj) |
| `timezones.json` | File that contains all time zone information |
| `timeZones.schema.json` | JSON schema for `timezones.json` |
| `StyleCop.json` | Code style (no real file, linked via *.csproj) |
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
#### Packages
| Package | Version |
| ------------------------------------------------------------------------------------- | ------- |
| [`StyleCop.Analyzers`](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) | 1.1.118 |
#### Projects
* `Wox.Infrastructure`
* `Wox.Plugin`

View File

@@ -86,6 +86,8 @@
<?define SystemComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.System.deps.json;Microsoft.PowerToys.Run.Plugin.System.dll?>
<?define TimeZoneComponentFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.TimeZone.deps.json;Microsoft.PowerToys.Run.Plugin.TimeZone.dll;PowerToys.ManagedTelemetry.dll?>
<?define SystemImagesComponentFiles=lock.dark.png;lock.light.png;logoff.dark.png;logoff.light.png;recyclebin.dark.png;recyclebin.light.png;restart.dark.png;restart.light.png;shutdown.dark.png;shutdown.light.png;sleep.dark.png;sleep.light.png;firmwareSettings.dark.png;firmwareSettings.light.png?>
<?define WinSetCmpFiles=plugin.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json;Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll;PowerToys.ManagedTelemetry.dll?>
@@ -477,6 +479,10 @@
<Directory Id="SystemPluginFolder" Name="System">
<Directory Id="SystemImagesFolder" Name="Images" />
</Directory>
<Directory Id="TimeZonePluginFolder" Name="TimeZone">
<Directory Id="TimeZoneImagesFolder" Name="Images" />
<Directory Id="TimeZoneLanguagesFolder" Name="Languages" />
</Directory>
<Directory Id="WindowsSettingsPluginFolder" Name="WindowsSettings">
<Directory Id="WindowsSettingsImagesFolder" Name="Images" />
<Directory Id="WindowsSettingsLanguagesFolder" Name="Languages" />
@@ -1085,7 +1091,7 @@
<Fragment>
<!-- Resource directories should be added only if the installer is built on the build farm -->
<?ifdef env.IsPipeline?>
<?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder?>
<?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeZonePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder?>
<DirectoryRef Id="$(var.ParentDirectory)">
<!-- Resource file directories -->
<?foreach Language in $(var.LocLanguageList)?>
@@ -1335,10 +1341,16 @@
Directory="Resource$(var.IdSafeLanguage)WindowsSettingsPluginFolder">
<File Id="Launcher_WindowsSettings_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\WindowsSettings\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsSettings.resources.dll" />
</Component>
<Component
Id="Launcher_TimeZone_$(var.IdSafeLanguage)_Component"
Guid="$(var.CompGUIDPrefix)14"
Directory="Resource$(var.IdSafeLanguage)TimeZonePluginFolder">
<File Id="Launcher_TimeZone_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\$(var.Language)\Microsoft.PowerToys.Run.Plugin.TimeZone.resources.dll" />
</Component>
<!-- Uncomment after Plugin receives the localization files.
<Component
Id="Launcher_WindowsTerminal_$(var.IdSafeLanguage)_Component"
Guid="$(var.CompGUIDPrefix)14"
Guid="$(var.CompGUIDPrefix)15"
Directory="Resource$(var.IdSafeLanguage)WindowsTerminalFolder">
<File Id="Launcher_WindowsTerminal_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\WindowsTerminal\$(var.Language)\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.resources.dll" />
</Component>
@@ -1549,7 +1561,20 @@
<File Id="SystemImagesComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\System\Images\$(var.File)" />
</Component>
<?endforeach?>
<!-- TimeZone Plugin -->
<?foreach File in $(var.TimeZoneComponentFiles)?>
<Component Id="TimeZoneComponent_$(var.File)" Win64="yes" Directory="TimeZonePluginFolder">
<File Id="TimeZoneComponentFile_$(var.File)" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\$(var.File)" />
</Component>
<?endforeach?>
<Component Id="TimeZoneImagesComponentLight" Directory="TimeZoneImagesFolder" >
<File Id="TimeZoneLightIcon" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\Images\timeZone.light.png" />
</Component>
<Component Id="TimeZoneImagesComponentDark" Directory="TimeZoneImagesFolder" >
<File Id="TimeZoneDarkIcon" Source="$(var.BinX64Dir)modules\launcher\Plugins\TimeZone\Images\timeZone.dark.png" />
</Component>
<!-- WindowsSettings Plugin -->
<?foreach File in $(var.WinSetCmpFiles)?>
<Component Id="WinSetCmp_$(var.File)" Win64="yes" Directory="WindowsSettingsPluginFolder">

View File

@@ -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.
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Classes
{
/// <summary>
/// A class that contains all time zones.
/// </summary>
public sealed class TimeZoneList
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeZoneList"/> class with empty properties.
/// </summary>
/// <remarks>
/// The standard constructor is need by the <see cref="JsonSerializer.Deserialize{TValue}(string, JsonSerializerOptions?)"/>-Method.
/// </remarks>
public TimeZoneList()
{
TimeZones = Enumerable.Empty<TimeZoneProperties>();
}
/// <summary>
/// Gets or sets a list with all time zones.
/// </summary>
public IEnumerable<TimeZoneProperties> TimeZones { get; set; }
}
}

View File

@@ -0,0 +1,96 @@
// 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
{
/// <summary>
/// A time zone
/// </summary>
public sealed class TimeZoneProperties
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeZoneProperties"/> class with empty properties.
/// </summary>
/// <remarks>
/// The standard constructor is need by the <see cref="JsonSerializer.Deserialize{TValue}(string, JsonSerializerOptions?)"/>-Method.
/// </remarks>
public TimeZoneProperties()
{
Offset = "00:00";
Name = string.Empty;
MilitaryName = string.Empty;
Shortcut = string.Empty;
TimeNamesStandard = Enumerable.Empty<string>();
TimeNamesDaylight = Enumerable.Empty<string>();
CountriesStandard = Enumerable.Empty<string>();
CountriesDaylight = Enumerable.Empty<string>();
ShortcutsStandard = Enumerable.Empty<string>();
ShortcutsDaylight = Enumerable.Empty<string>();
}
/// <summary>
/// Gets or sets the time offset of this time zone (the gap from the UTC time zone)
/// </summary>
public string Offset { get; set; }
/// <summary>
/// Gets or sets the name of this time zone.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the military name of this time zone.
/// </summary>
public string MilitaryName { get; set; }
/// <summary>
/// Gets or sets the shortcuts of the name this time zone.
/// </summary>
public string Shortcut { get; set; }
/// <summary>
/// Gets or sets a list with names for the standard time.
/// </summary>
public IEnumerable<string> TimeNamesStandard { get; set; }
/// <summary>
/// Gets or sets a list with names for the daylight saving time.
/// </summary>
public IEnumerable<string> TimeNamesDaylight { get; set; }
/// <summary>
/// Gets or sets a list with all countries in this time zone that don't use a daylight saving time.
/// </summary>
public IEnumerable<string> CountriesStandard { get; set; }
/// <summary>
/// Gets or sets a list with all countries in this time zone that use a daylight saving time.
/// </summary>
public IEnumerable<string> CountriesDaylight { get; set; }
/// <summary>
/// Gets or sets a list with shortcuts for the names for the standard time.
/// </summary>
public IEnumerable<string> ShortcutsStandard { get; set; }
/// <summary>
/// Gets or sets a list with shortcuts for the names for the daylight saving time.
/// </summary>
public IEnumerable<string> ShortcutsDaylight { get; set; }
/// <summary>
/// Gets a compatible <see cref="TimeSpan"/> of the <see cref="Offset"/>.
/// </summary>
internal TimeSpan OffsetAsTimeSpan
{
get { return TimeSpan.TryParse(Offset, out var result) ? result : new TimeSpan(0, 0, 0); }
}
}
}

View File

@@ -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.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
{
/// <summary>
/// Additional settings for the time zone plugin.
/// </summary>
internal sealed class TimeZoneSettings
{
/// <summary>
/// Gets or sets a value indicating whether the time zone name of a time zone is shown in the results.
/// </summary>
internal bool ShowTimeZoneNames { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the time name of a time zone is shown in the results.
/// </summary>
internal bool ShowTimeNames { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the military name of a time zone is shown in the results.
/// </summary>
internal bool ShowMilitaryTimeZoneNames { get; set; }
/// <summary>
/// Return a list with all settings. Additional
/// </summary>
/// <returns>A list with all settings.</returns>
internal static List<PluginAdditionalOption> GetAdditionalOptions()
{
var optionList = new List<PluginAdditionalOption>
{
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;
}
/// <summary>
/// Update this settings.
/// </summary>
/// <param name="settings">The settings for all power launcher plugin.</param>
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");
}
/// <summary>
/// Return one <see cref="bool"/> setting of the given settings list with the given name.
/// </summary>
/// <param name="settings">The object that contain all settings.</param>
/// <param name="name">The name of the setting.</param>
/// <returns>A settings value.</returns>
private static bool GetSettingOrDefault(PowerLauncherPluginSettings settings, string name)
{
var option = settings.AdditionalOptions.FirstOrDefault(x => x.Key == name);
// As a fallback 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;
}
}
}

View File

@@ -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 System.Text;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Extensions
{
/// <summary>
/// Extensions for <see cref="StringBuilder"/>-Objects
/// </summary>
internal static class StringBuilderExtensions
{
/// <summary>
/// Save append the given <see cref="string"/> value with the given maximum length to the <see cref="StringBuilder"/>
/// </summary>
/// <param name="stringBuilder">The <see cref="StringBuilder"/> to append the string.</param>
/// <param name="value">The value that should be append.</param>
/// <param name="maxLength">The max length of the <see cref="string"/> value that should append.</param>
internal static void SaveAppend(this StringBuilder stringBuilder, string value, int maxLength)
{
if (value.Length > maxLength)
{
stringBuilder.Append(value, 0, maxLength);
}
else
{
stringBuilder.Append(value);
}
}
/// <summary>
/// Cut too long texts to the given length and add three dots at the end of the text.
/// </summary>
/// <param name="stringBuilder">The <see cref="StringBuilder"/> that contain the text.</param>
/// <param name="maxLength">The maximum length for the text, inclusive the three dots.</param>
internal static void CutTooLong(this StringBuilder stringBuilder, int maxLength)
{
if (stringBuilder.Length <= maxLength)
{
return;
}
stringBuilder.Length = maxLength - 3;
stringBuilder.Append('.');
stringBuilder.Append('.');
stringBuilder.Append('.');
}
}
}

View File

@@ -0,0 +1,77 @@
// 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 System.Windows.Media.Animation;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Classes;
using Microsoft.PowerToys.Run.Plugin.TimeZone.Properties;
using Wox.Plugin;
using Wox.Plugin.Logger;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper
{
/// <summary>
/// Helper class to easier work with context menu entries
/// </summary>
internal static class ContextMenuHelper
{
/// <summary>
/// Return a list with all context menu entries for the given <see cref="Result"/>
/// <para>Symbols taken from <see href="https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font"/></para>
/// </summary>
/// <param name="result">The result for the context menu entires</param>
/// <param name="assemblyName">The name of the this assembly</param>
/// <returns>A list with context menu entries</returns>
internal static List<ContextMenuResult> GetContextMenu(in Result result, in string assemblyName)
{
if (!(result?.ContextData is DateTime dateTime))
{
return new List<ContextMenuResult>(0);
}
var list = new List<ContextMenuResult>
{
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;
}
#pragma warning disable CA1031 // Do not catch general exception types
/// <summary>
/// Copy the given text to the clipboard
/// </summary>
/// <param name="text">The text to copy to the clipboard</param>
/// <returns><see langword="true"/>The text successful copy to the clipboard, otherwise <see langword="false"/></returns>
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;
}
}
#pragma warning restore CA1031 // Do not catch general exception types
}
}

View File

@@ -0,0 +1,62 @@
// 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
{
/// <summary>
/// Helper class to easier work with the JSON files.
/// </summary>
internal static class JsonHelper
{
/// <summary>
/// The name of the file that contains all time zones.
/// </summary>
private const string _settingsFile = "timeZones.json";
/// <summary>
/// Read all possible time zones.
/// </summary>
/// <returns>A object that contain a list with time zones.</returns>
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)
{
throw new Exception("stream is null");
}
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
settings = JsonSerializer.Deserialize<TimeZoneList>(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();
}
}
}

View File

@@ -0,0 +1,848 @@
// 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.Diagnostics;
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 Mono.Collections.Generic;
using Wox.Plugin;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone.Helper
{
/// <summary>
/// Helper class to easier work with results
/// </summary>
internal static class ResultHelper
{
/// <summary>
/// Return a list of <see cref="Result"/>s based on the given <see cref="Query"/>.
/// </summary>
/// <param name="timeZones">A list with all possible time zones.</param>
/// <param name="options">Additional options to limit the results.</param>
/// <param name="query">The <see cref="Query"/> to filter the <see cref="Results"/>.</param>
/// <param name="iconPath">The path to the icon that is used for each result.</param>
/// <returns>A list with <see cref="Result"/>s.</returns>
internal static IEnumerable<Result> GetResults(in IEnumerable<TimeZoneProperties> timeZones, in TimeZoneSettings options, in Query query, in string iconPath)
{
var results = new List<Result>();
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;
}
/// <summary>
/// Return a list with <see cref="Result"/> based on the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the information for the <see cref="Result"/>.</param>
/// <param name="options">Additional options to limit the results.</param>
/// <param name="query">The <see cref="Query"/> that should match.</param>
/// <param name="iconPath">The path to the icon that is used for each result.</param>
/// <param name="dateTime">The current time in UTC for the <see cref="Result"/>.</param>
/// <returns>A list with <see cref="Result"/>.</returns>
private static IEnumerable<Result> GetResults(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings options, in Query query, in string iconPath, in DateTime dateTime)
{
var results = new Collection<Result>();
var standardTitle = GetTitle(timeZoneProperties, options, query, dateTime, false);
var daylightTitle = GetTitle(timeZoneProperties, options, query, dateTime, true);
if (standardTitle.Equals(daylightTitle))
{
results.Add(new Result
{
ContextData = GetTimeInTimeZone(timeZoneProperties, dateTime, false),
IcoPath = iconPath,
Title = standardTitle.ToString(),
SubTitle = GetAllCountries(timeZoneProperties, query, maxLength: 100).ToString(),
ToolTipData = new ToolTipData(standardTitle.ToString(), GetAllToolTip(timeZoneProperties, options).ToString()),
});
return results;
}
if (!MatchTimeZoneShortcut(timeZoneProperties, query)
&& !MatchTimeZoneNames(timeZoneProperties, query)
&& !MatchOffset(timeZoneProperties, query))
{
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 = standardTitle.ToString(),
ToolTipData = new ToolTipData(standardTitle.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 = daylightTitle.ToString(),
ToolTipData = new ToolTipData(daylightTitle.ToString(), GetDaylightToolTip(timeZoneProperties, options).ToString()),
});
}
return results;
}
/// <summary>
/// Return the current local time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="dateTime">The current time in UTC.</param>
/// <param name="daylightSavingTime">indicate that the result is for a time zone that use a daylight saving time.</param>
/// <returns>The current local time in a time zone.</returns>
private 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;
}
/// <summary>
/// Return the title for the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the results.</param>
/// <param name="query">The <see cref="Query"/> that should match.</param>
/// <param name="dateTime">The current time in UTC.</param>
/// <param name="daylightSavingTime">indicate that the result is for a time zone that use a daylight saving time.</param>
/// <returns>A title for a time zone.</returns>
private 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;
}
/// <summary>
/// Return a tool-tip for the given time zone with countries that use the standard time.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the results.</param>
/// <returns>A tool-tip with countries that use the standard time.</returns>
private 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;
}
/// <summary>
/// Return a tool-tip for the given time zone with countries that use the daylight saving time.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the type of the names.</param>
/// <returns>A tool-tip with countries that use the daylight saving time.</returns>
private 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;
}
/// <summary>
/// Return a tool-tip for the given time zone with countries.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain all information.</param>
/// <param name="timeZoneSettings">Additional options to limit the type of the names.</param>
/// <returns>A tool-tip with countries.</returns>
private 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;
}
/// <summary>
/// Return all names of the given time zone that match the given query.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain a hand of names.</param>
/// <param name="timeZoneSettings">Additional options to limit the type of the names.</param>
/// <param name="query">The query that should match.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All know names of the given time zone.</returns>
private static StringBuilder GetNames(in TimeZoneProperties timeZoneProperties, in TimeZoneSettings timeZoneSettings, Query? query, in int maxLength)
{
var allNames = new List<string>();
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<string> names;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
names = allNames;
}
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
{
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;
}
/// <summary>
/// Return all standard time names shortcuts of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain a hand of names.</param>
/// <returns>All standard time names shortcuts of the given time zone.</returns>
private 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;
}
/// <summary>
/// Return all know daylight time names shortcuts of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain a hand of names.</param>
/// <returns>All know daylight time names shortcuts of the given time zone.</returns>
private 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;
}
/// <summary>
/// Return all countries that use the standard time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the countries.</param>
/// <param name="query">The <see cref="Query"/> that should match a country that use standard time.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All countries that use the standard time of the given time zone.</returns>
private static StringBuilder GetStandardCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength)
{
IEnumerable<string> countries;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
countries = timeZoneProperties.CountriesStandard;
}
else
{
countries = timeZoneProperties.CountriesStandard.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase));
}
var stringBuilder = new StringBuilder();
var lastEntry = countries.LastOrDefault();
foreach (var country in countries)
{
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;
}
/// <summary>
/// Return all countries that use the daylight saving time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the countries.</param>
/// <param name="query">The <see cref="Query"/> that should match a country that use daylight time.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All countries that use the daylight saving time of the given time zone.</returns>
private static StringBuilder GetDaylightCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength)
{
IEnumerable<string> countries;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
countries = timeZoneProperties.CountriesDaylight;
}
else
{
countries = timeZoneProperties.CountriesDaylight.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase));
}
var stringBuilder = new StringBuilder();
var lastEntry = countries.LastOrDefault();
foreach (var country in countries)
{
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;
}
/// <summary>
/// Return all countries of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone that contain the countries.</param>
/// <param name="query">The <see cref="Query"/> that should match a country that use standard or daylight time.</param>
/// <param name="maxLength">The maximum length of the result.</param>
/// <returns>All countries of the given time zone.</returns>
private static StringBuilder GetAllCountries(in TimeZoneProperties timeZoneProperties, Query? query, in int maxLength)
{
IEnumerable<string> countries;
if (query is null || string.IsNullOrWhiteSpace(query.Search))
{
countries = timeZoneProperties.CountriesDaylight.Concat(timeZoneProperties.CountriesStandard);
}
else
{
countries = timeZoneProperties.CountriesDaylight.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase))
.Concat(timeZoneProperties.CountriesStandard.Where(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)));
}
var stringBuilder = new StringBuilder();
var lastEntry = countries.LastOrDefault();
foreach (var country in countries)
{
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;
}
/// <summary>
/// Indicate that the given query match the time zone shortcut of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchTimeZoneShortcut(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.Shortcut.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase);
return result;
}
/// <summary>
/// Indicate that the given query match one of the time zone names of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private 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;
}
/// <summary>
/// Indicate that the given query match the offset of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private 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;
}
/// <summary>
/// Indicate that the given query match one of the standard time names of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchStandardTimeNames(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.TimeNamesDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match one of the daylight time names of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchDaylightTimeNames(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.TimeNamesDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match one of the countries that use the standard time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchStandardCountries(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.CountriesStandard?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match one of the countries that use the daylight time of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchDaylightCountries(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.CountriesDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match the time zone shortcut of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchStandardTimeShortcuts(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.ShortcutsStandard?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
/// <summary>
/// Indicate that the given query match the time zone shortcut of the given time zone.
/// </summary>
/// <param name="timeZoneProperties">The time zone to check.</param>
/// <param name="query">The query that should match.</param>
/// <returns><see langword="true"/>if the query match, otherwise <see langword="false"/>.</returns>
private static bool MatchDaylightTimeShortcuts(in TimeZoneProperties timeZoneProperties, Query query)
{
var result = timeZoneProperties.ShortcutsDaylight?.Any(x => x.Contains(query.Search, StringComparison.CurrentCultureIgnoreCase)) == true;
return result;
}
}
}

View File

@@ -0,0 +1,133 @@
// 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
{
/// <summary>
/// Helper class to easier work with translations.
/// </summary>
internal static class TranslationHelper
{
/// <summary>
/// Translate all names and countries of the <see cref="TimeZoneList"/> class.
/// </summary>
/// <param name="timeZoneList">A class that contain all possible time zones.</param>
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<string>();
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<string>();
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<string>();
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<string>();
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;
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,217 @@
// 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.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;
namespace Microsoft.PowerToys.Run.Plugin.TimeZone
{
/// <summary>
/// A power launcher plugin to search across time zones.
/// </summary>
public class Main : IPlugin, IContextMenu, IPluginI18n, ISettingProvider, IDisposable
{
/// <summary>
/// The name of this assembly
/// </summary>
private readonly string _assemblyName;
/// <summary>
/// The settings for this plugin.
/// </summary>
private readonly TimeZoneSettings _timeZoneSettings;
/// <summary>
/// The initial context for this plugin (contains API and meta-data)
/// </summary>
private PluginInitContext? _context;
/// <summary>
/// The path to the icon for each result
/// </summary>
private string _defaultIconPath;
/// <summary>
/// Indicate that the plugin is disposed
/// </summary>
private bool _disposed;
/// <summary>
/// A class that contain all possible time zones.
/// </summary>
private TimeZoneList? _timeZoneList;
/// <summary>
/// Initializes a new instance of the <see cref="Main"/> class.
/// </summary>
public Main()
{
_assemblyName = Assembly.GetExecutingAssembly().GetName().Name ?? GetTranslatedPluginTitle();
_defaultIconPath = "Images/timeZone.light.png";
_timeZoneSettings = new TimeZoneSettings();
}
/// <summary>
/// Gets the localized name.
/// </summary>
public string Name
{
get { return Resources.PluginTitle; }
}
/// <summary>
/// Gets the localized description.
/// </summary>
public string Description
{
get { return Resources.PluginDescription; }
}
/// <summary>
/// Gets the additional options for this plugin.
/// </summary>
public IEnumerable<PluginAdditionalOption> AdditionalOptions
{
get { return TimeZoneSettings.GetAdditionalOptions(); }
}
/// <summary>
/// Initialize the plugin with the given <see cref="PluginInitContext"/>
/// </summary>
/// <param name="context">The <see cref="PluginInitContext"/> for this plugin</param>
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);
}
/// <summary>
/// Return a filtered list, based on the given query
/// </summary>
/// <param name="query">The query to filter the list</param>
/// <returns>A filtered list, can be empty when nothing was found</returns>
public List<Result> Query(Query query)
{
if (_timeZoneList?.TimeZones is null)
{
return new List<Result>(0);
}
if (query is null)
{
return new List<Result>(0);
}
var results = ResultHelper.GetResults(_timeZoneList.TimeZones, _timeZoneSettings, query, _defaultIconPath);
return results.ToList();
}
/// <summary>
/// Return a list context menu entries for a given <see cref="Result"/> (shown at the right side of the result)
/// </summary>
/// <param name="selectedResult">The <see cref="Result"/> for the list with context menu entries</param>
/// <returns>A list context menu entries</returns>
public List<ContextMenuResult> LoadContextMenus(Result selectedResult)
{
return ContextMenuHelper.GetContextMenu(selectedResult, _assemblyName);
}
/// <summary>
/// Change all theme-based elements (typical called when the plugin theme has changed)
/// </summary>
/// <param name="oldtheme">The old <see cref="Theme"/></param>
/// <param name="newTheme">The new <see cref="Theme"/></param>
private void OnThemeChanged(Theme oldtheme, Theme newTheme)
{
UpdateIconPath(newTheme);
}
/// <summary>
/// Update all icons (typical called when the plugin theme has changed)
/// </summary>
/// <param name="theme">The new <see cref="Theme"/> for the icons</param>
private void UpdateIconPath(Theme theme)
{
_defaultIconPath = theme == Theme.Light || theme == Theme.HighContrastWhite
? "Images/timeZone.light.png"
: "Images/timeZone.dark.png";
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Wrapper method for <see cref="Dispose"/> that dispose additional objects and events form the plugin itself
/// </summary>
/// <param name="disposing">Indicate that the plugin is disposed</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed || !disposing)
{
return;
}
if (!(_context is null))
{
_context.API.ThemeChanged -= OnThemeChanged;
}
_disposed = true;
}
/// <summary>
/// Return the translated plugin title.
/// </summary>
/// <returns>A translated plugin title.</returns>
public string GetTranslatedPluginTitle()
{
return Resources.PluginTitle;
}
/// <summary>
/// Return the translated plugin description.
/// </summary>
/// <returns>A translated plugin description.</returns>
public string GetTranslatedPluginDescription()
{
return Resources.PluginDescription;
}
/// <summary>
/// Return a additional setting panel for this plugin.
/// </summary>
/// <returns>A additional setting panel.</returns>
public Control CreateSettingPanel()
{
throw new NotImplementedException();
}
/// <summary>
/// Update the plugin settings
/// </summary>
/// <param name="settings">The settings for all power launcher plugin.</param>
public void UpdateSettings(PowerLauncherPluginSettings settings)
{
_timeZoneSettings.UpdateSettings(settings);
}
}
}

View File

@@ -0,0 +1,119 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Version.props" />
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<RootNamespace>Microsoft.PowerToys.Run.Plugin.TimeZone</RootNamespace>
<AssemblyName>Microsoft.PowerToys.Run.Plugin.TimeZone</AssemblyName>
<Version>$(Version).0</Version>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Platforms>x64</Platforms>
<NeutralLanguage>en-US</NeutralLanguage>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\..\..\x64\Debug\modules\launcher\Plugins\TimeZone\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutputPath>..\..\..\..\..\x64\Release\modules\launcher\Plugins\TimeZone\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Languages\**" />
<EmbeddedResource Remove="Languages\**" />
<None Remove="Languages\**" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\codeAnalysis\GlobalSuppressions.cs">
<Link>GlobalSuppressions.cs</Link>
</Compile>
<AdditionalFiles Include="..\..\..\..\codeAnalysis\StyleCop.json">
<Link>StyleCop.json</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="timeZones.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\Wox.Plugin\Wox.Plugin.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Images\timeZone.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\timeZone.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
{
"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"
}

View File

@@ -0,0 +1,80 @@
{
"$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"
}
}
}
}
}
}
}