From e4b6e851c015e1dafa0b895932f3e0b5925510b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=93=9D=E7=82=B9lilac?= Date: Mon, 22 Feb 2021 00:59:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B03.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ContextMenuManager/AppConfig.cs | 56 +- ContextMenuManager/AppImage.cs | 14 +- ContextMenuManager/AppString.cs | 78 +- .../IconDialog.cs | 2 +- .../InputDialog.cs | 4 +- .../MyCheckBox.cs | 3 +- .../MyListBox.cs | 11 +- .../MyMainForm.cs | 4 +- .../MySideBar.cs | 12 +- .../MyStatusBar.cs | 24 +- .../MyToolBar.cs | 17 +- .../MyToolTip.cs | 6 +- .../PictureButton.cs | 2 +- .../ReadOnlyRichTextBox.cs | 10 +- .../ResizbleForm.cs | 4 +- .../ControlExtension.cs | 2 +- .../BluePointLilac.Methods/DesktopIni.cs | 34 + .../DirectoryEx.cs | 2 +- .../EncodingType.cs | 17 +- .../BluePointLilac.Methods/ExternalProgram.cs | 177 +++++ .../FileExtension.cs | 2 +- .../GuidEx.cs | 14 +- .../HighDpi.cs | 2 +- .../ImageExtension.cs | 2 +- .../IniReader.cs | 3 +- .../IniWriter.cs | 6 +- .../MessageBoxEx.cs | 2 +- .../ObjectPath.cs | 98 +-- .../RegTrustedInstaller.cs | 19 +- .../RegistryEx.cs | 7 +- .../ResourceIcon.cs | 2 +- .../ResourceString.cs | 2 +- .../RichTextBoxExtension.cs | 2 +- .../BluePointLilac.Methods/SingleInstance.cs | 51 ++ .../StringExtension.cs | 2 +- .../TextBoxExtension.cs | 2 +- .../WindowsOsVersion.cs | 2 +- .../BluePointLilac.Methods/WshShortcut.cs | 202 +++++ .../PropertiesDialog.cs | 56 -- .../BulePointLilac.Methods/WshShortcut.cs | 91 --- ContextMenuManager/ContextMenuManager.csproj | 124 +-- ContextMenuManager/Controls/AboutApp.cs | 109 ++- .../Controls/AddGuidDicDialog.cs | 4 +- ContextMenuManager/Controls/CommandDialog.cs | 107 --- .../Controls/EnhanceMenusItem.cs | 62 +- .../Controls/EnhanceMenusList.cs | 195 +++-- .../Controls/ExplorerRestarter.cs | 43 +- .../Controls/FileExtensionDialog.cs | 89 --- .../Controls/GuidBlockedItem.cs | 6 +- .../Controls/GuidBlockedList.cs | 18 +- ContextMenuManager/Controls/IEItem.cs | 122 +++ ContextMenuManager/Controls/IEList.cs | 63 ++ .../Controls/Interfaces/IBtnDeleteItem.cs | 4 +- .../Controls/Interfaces/IBtnMoveUpDownItem.cs | 2 +- .../Controls/Interfaces/IBtnOpenPathItem.cs | 19 +- .../Controls/Interfaces/IBtnShowMenuItem.cs | 2 +- .../Controls/Interfaces/IChkVisibleItem.cs | 2 +- .../Controls/Interfaces/IFoldGroupItem.cs | 23 +- .../Interfaces/ITsiAdministratorItem.cs | 46 ++ .../Controls/Interfaces/ITsiCommandItem.cs | 4 +- .../Controls/Interfaces/ITsiDeleteItem.cs | 27 +- .../Controls/Interfaces/ITsiFilePathItem.cs | 17 +- .../Controls/Interfaces/ITsiIconItem.cs | 4 +- .../Controls/Interfaces/ITsiRegExportItem.cs | 2 +- .../Controls/Interfaces/ITsiRegPathItem.cs | 9 +- .../Interfaces/ITsiShortcutCommandItem.cs | 138 ++++ .../Controls/Interfaces/ITsiTextItem.cs | 4 +- .../Controls/Interfaces/ITsiWebSearchItem.cs | 15 +- ContextMenuManager/Controls/LockNewItem.cs | 103 --- ContextMenuManager/Controls/NewIEDialog.cs | 64 ++ ContextMenuManager/Controls/NewItem.cs | 13 +- ContextMenuManager/Controls/NewItemForm.cs | 28 +- ...NewSendToDialog.cs => NewLnkFileDialog.cs} | 89 ++- .../Controls/NewOpenWithDialog.cs | 42 +- ContextMenuManager/Controls/NewShellDialog.cs | 57 +- ContextMenuManager/Controls/OpenWithItem.cs | 13 +- ContextMenuManager/Controls/OpenWithList.cs | 10 +- ContextMenuManager/Controls/RegRuleItem.cs | 230 ------ ContextMenuManager/Controls/RuleItem.cs | 498 ++++++++++++ ContextMenuManager/Controls/SelectDialog.cs | 147 ++++ ContextMenuManager/Controls/SendToItem.cs | 88 ++- ContextMenuManager/Controls/SendToList.cs | 38 +- ContextMenuManager/Controls/ShellExItem.cs | 51 +- .../Controls/ShellExecuteDialog.cs | 162 ++++ ContextMenuManager/Controls/ShellItem.cs | 109 ++- ContextMenuManager/Controls/ShellList.cs | 469 ++++++++---- ContextMenuManager/Controls/ShellNewItem.cs | 77 +- ContextMenuManager/Controls/ShellNewList.cs | 186 +++-- .../Controls/ShellStoreDialog.cs | 43 +- .../Controls/ShellSubMenuDialog.cs | 65 +- ContextMenuManager/Controls/ThirdRulesList.cs | 144 +++- ContextMenuManager/Controls/WinXGroupItem.cs | 111 +++ ContextMenuManager/Controls/WinXItem.cs | 152 +++- ContextMenuManager/Controls/WinXList.cs | 201 ++++- ContextMenuManager/GuidInfo.cs | 4 +- ContextMenuManager/MainForm.cs | 108 +-- ContextMenuManager/Program.cs | 7 +- ContextMenuManager/Properties/AssemblyInfo.cs | 6 +- .../Properties/Resources.Designer.cs | 85 +- ContextMenuManager/Properties/Resources.resx | 20 +- .../Resources/HashLnk/HashLnk_32.exe | Bin 0 -> 116736 bytes .../Resources/HashLnk/HashLnk_64.exe | Bin 0 -> 137728 bytes .../Images/{CustomType.png => Custom.png} | Bin .../Properties/Resources/Images/NewFolder.png | Bin 0 -> 706 bytes .../Images/{Types.png => Select.png} | Bin .../Resources/Images/SeparatorItem.png | Bin 914 -> 0 bytes .../Resources/Texts/AppLanguageDic.ini | 108 +-- .../Resources/Texts/EnhanceMenusDic.xml | 723 ++++++++++++++++-- .../Resources/Texts/GuidInfosDic.ini | 60 +- .../Resources/Texts/ThirdRulesDic.xml | 356 +++++---- .../Properties/Settings.Designer.cs | 2 +- ContextMenuManager/Updater.cs | 12 +- Donate.md | 9 +- README.md | 26 +- languages/zh-CN.ini | 108 +-- 115 files changed, 5044 insertions(+), 2086 deletions(-) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/IconDialog.cs (96%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/InputDialog.cs (97%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MyCheckBox.cs (88%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MyListBox.cs (97%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MyMainForm.cs (94%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MySideBar.cs (95%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MyStatusBar.cs (68%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MyToolBar.cs (86%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/MyToolTip.cs (84%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/PictureButton.cs (96%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/ReadOnlyRichTextBox.cs (78%) rename ContextMenuManager/{BulePointLilac.Controls => BluePointLilac.Controls}/ResizbleForm.cs (97%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/ControlExtension.cs (98%) create mode 100644 ContextMenuManager/BluePointLilac.Methods/DesktopIni.cs rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/DirectoryEx.cs (95%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/EncodingType.cs (81%) create mode 100644 ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/FileExtension.cs (94%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/GuidEx.cs (54%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/HighDpi.cs (97%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/ImageExtension.cs (98%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/IniReader.cs (97%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/IniWriter.cs (97%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/MessageBoxEx.cs (92%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/ObjectPath.cs (63%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/RegTrustedInstaller.cs (91%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/RegistryEx.cs (96%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/ResourceIcon.cs (99%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/ResourceString.cs (96%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/RichTextBoxExtension.cs (99%) create mode 100644 ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/StringExtension.cs (90%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/TextBoxExtension.cs (95%) rename ContextMenuManager/{BulePointLilac.Methods => BluePointLilac.Methods}/WindowsOsVersion.cs (98%) create mode 100644 ContextMenuManager/BluePointLilac.Methods/WshShortcut.cs delete mode 100644 ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs delete mode 100644 ContextMenuManager/BulePointLilac.Methods/WshShortcut.cs delete mode 100644 ContextMenuManager/Controls/CommandDialog.cs delete mode 100644 ContextMenuManager/Controls/FileExtensionDialog.cs create mode 100644 ContextMenuManager/Controls/IEItem.cs create mode 100644 ContextMenuManager/Controls/IEList.cs create mode 100644 ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs create mode 100644 ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs delete mode 100644 ContextMenuManager/Controls/LockNewItem.cs create mode 100644 ContextMenuManager/Controls/NewIEDialog.cs rename ContextMenuManager/Controls/{NewSendToDialog.cs => NewLnkFileDialog.cs} (51%) delete mode 100644 ContextMenuManager/Controls/RegRuleItem.cs create mode 100644 ContextMenuManager/Controls/RuleItem.cs create mode 100644 ContextMenuManager/Controls/SelectDialog.cs create mode 100644 ContextMenuManager/Controls/ShellExecuteDialog.cs create mode 100644 ContextMenuManager/Controls/WinXGroupItem.cs create mode 100644 ContextMenuManager/Properties/Resources/HashLnk/HashLnk_32.exe create mode 100644 ContextMenuManager/Properties/Resources/HashLnk/HashLnk_64.exe rename ContextMenuManager/Properties/Resources/Images/{CustomType.png => Custom.png} (100%) create mode 100644 ContextMenuManager/Properties/Resources/Images/NewFolder.png rename ContextMenuManager/Properties/Resources/Images/{Types.png => Select.png} (100%) delete mode 100644 ContextMenuManager/Properties/Resources/Images/SeparatorItem.png diff --git a/ContextMenuManager/AppConfig.cs b/ContextMenuManager/AppConfig.cs index df8d526..576d4f8 100644 --- a/ContextMenuManager/AppConfig.cs +++ b/ContextMenuManager/AppConfig.cs @@ -3,7 +3,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; -using BulePointLilac.Methods; +using BluePointLilac.Methods; namespace ContextMenuManager { @@ -11,8 +11,10 @@ static class AppConfig { static AppConfig() { - CreateDirs(); - DeleteOldFiles(); + foreach(string dirPath in new[] { ConfigDir, ProgramsDir, BackupDir, LangsDir, DicsDir, WebDicsDir, UserDicsDir }) + { + Directory.CreateDirectory(dirPath); + } } [DllImport("kernel32.dll")] @@ -25,6 +27,7 @@ static AppConfig() public static string ConfigIni = $@"{ConfigDir}\Config.ini"; public static string BackupDir = $@"{ConfigDir}\Backup"; public static string LangsDir = $@"{ConfigDir}\Languages"; + public static string ProgramsDir = $@"{ConfigDir}\Programs"; public static string DicsDir = $@"{ConfigDir}\Dictionaries"; public static string WebDicsDir = $@"{DicsDir}\Web"; public static string UserDicsDir = $@"{DicsDir}\User"; @@ -34,7 +37,7 @@ static AppConfig() public static string UserThirdRulesDic = $@"{UserDicsDir}\{ThIRDRULESDICXML}"; public static string WebEnhanceMenusDic = $@"{WebDicsDir}\{ENHANCEMENUSICXML}"; public static string UserEnhanceMenusDic = $@"{UserDicsDir}\{ENHANCEMENUSICXML}"; - + public static string HashLnkExePath = $@"{ProgramsDir}\HashLnk.exe"; public const string ZH_CNINI = "zh-CN.ini"; public const string GUIDINFOSDICINI = "GuidInfosDic.ini"; public const string ThIRDRULESDICXML = "ThirdRulesDic.xml"; @@ -120,43 +123,22 @@ public static string EngineUrl } } - private static void CreateDirs() + public static bool ShowFilePath { - foreach(string dirPath in new[] { ConfigDir, BackupDir, LangsDir, DicsDir, WebDicsDir, UserDicsDir }) - { - Directory.CreateDirectory(dirPath); - } + get => ConfigWriter.GetValue("General", "ShowFilePath") == "1"; + set => ConfigWriter.SetValue("General", "ShowFilePath", (value ? 1 : 0).ToString()); } - private static void DeleteOldFiles() + public static bool WinXSortable { - DirectoryInfo configDi = new DirectoryInfo(ConfigDir); - foreach(DirectoryInfo di in configDi.GetDirectories()) - { - bool isOther = true; - foreach(string path in new[] { BackupDir, LangsDir, DicsDir }) - { - if(di.FullName.Equals(path, StringComparison.OrdinalIgnoreCase)) - { - isOther = false; - break; - } - } - if(isOther) Directory.Delete(di.FullName); - } - foreach(FileInfo fi in configDi.GetFiles()) - { - bool isOther = true; - foreach(string path in new[] { ConfigIni }) - { - if(fi.FullName.Equals(path, StringComparison.OrdinalIgnoreCase)) - { - isOther = false; - break; - } - } - if(isOther) File.Delete(fi.FullName); - } + get => ConfigWriter.GetValue("General", "WinXSortable") == "1"; + set => ConfigWriter.SetValue("General", "WinXSortable", (value ? 1 : 0).ToString()); + } + + public static bool OpenMoreRegedit + { + get => ConfigWriter.GetValue("General", "OpenMoreRegedit") == "1"; + set => ConfigWriter.SetValue("General", "OpenMoreRegedit", (value ? 1 : 0).ToString()); } } } \ No newline at end of file diff --git a/ContextMenuManager/AppImage.cs b/ContextMenuManager/AppImage.cs index 5f8ecd4..7b65660 100644 --- a/ContextMenuManager/AppImage.cs +++ b/ContextMenuManager/AppImage.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using ContextMenuManager.Properties; using System.Drawing; @@ -41,12 +41,12 @@ public static class AppImage public static readonly Image Down = Up.RotateImage(RotateFlipType.Rotate180FlipNone); ///新建项目图标 public static readonly Image NewItem = Resources.NewItem.ResizeImage(Scale); - ///分隔线图标 - public static readonly Image Separator = Resources.SeparatorItem.ResizeImage(Scale); - ///自定义类型图标 - public static readonly Image CustomType = Resources.CustomType.ResizeImage(Scale); - ///所有文件类型图标 - public static readonly Image Types = Resources.Types.ResizeImage(Scale); + ///新建文件夹图标 + public static readonly Image NewFolder = Resources.NewFolder.ResizeImage(Scale); + ///自定义图标 + public static readonly Image Custom = Resources.Custom.ResizeImage(Scale); + ///选择图标 + public static readonly Image Select = Resources.Select.ResizeImage(Scale); ///Microsoft Store图标 public static readonly Image MicrosoftStore = Resources.MicrosoftStore.ResizeImage(Scale); ///Skype图标 diff --git a/ContextMenuManager/AppString.cs b/ContextMenuManager/AppString.cs index d93cd38..f36d409 100644 --- a/ContextMenuManager/AppString.cs +++ b/ContextMenuManager/AppString.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System.Text; namespace ContextMenuManager @@ -55,20 +55,16 @@ public static class SideBar public static string LnkFile => GetValue("LnkFile"); public static string UwpLnk => GetValue("UwpLnk"); public static string ExeFile => GetValue("ExeFile"); - public static string TextFile => GetValue("TextFile"); - public static string DocumentFile => GetValue("DocumentFile"); - public static string ImageFile => GetValue("ImageFile"); - public static string VideoFile => GetValue("VideoFile"); - public static string AudioFile => GetValue("AudioFile"); - public static string ImageDirectory => GetValue("ImageDirectory"); - public static string VideoDirectory => GetValue("VideoDirectory"); - public static string AudioDirectory => GetValue("AudioDirectory"); public static string UnknownType => GetValue("UnknownType"); - public static string CustomType => GetValue("CustomType"); + public static string CustomExtension => GetValue("CustomExtension"); + public static string PerceivedType => GetValue("PerceivedType"); + public static string DirectoryType => GetValue("DirectoryType"); public static string EnhanceMenu => GetValue("EnhanceMenu"); public static string ThirdRules => GetValue("ThirdRules"); public static string GuidBlocked => GetValue("GuidBlocked"); + public static string DragDrop => GetValue("DragDrop"); public static string PublicReferences => GetValue("PublicReferences"); + public static string IEMenu => GetValue("IEMenu"); public static string AppSetting => GetValue("AppSetting"); public static string AboutApp => GetValue("AboutApp"); public static string Dictionaries => GetValue("Dictionaries"); @@ -97,23 +93,19 @@ public static class StatusBar public static string LnkFile => GetValue("LnkFile"); public static string UwpLnk => GetValue("UwpLnk"); public static string ExeFile => GetValue("ExeFile"); - public static string TextFile => GetValue("TextFile"); - public static string DocumentFile => GetValue("DocumentFile"); - public static string ImageFile => GetValue("ImageFile"); - public static string VideoFile => GetValue("VideoFile"); - public static string AudioFile => GetValue("AudioFile"); - public static string ImageDirectory => GetValue("ImageDirectory"); - public static string VideoDirectory => GetValue("VideoDirectory"); - public static string AudioDirectory => GetValue("AudioDirectory"); public static string UnknownType => GetValue("UnknownType"); - public static string CustomType => GetValue("CustomType"); + public static string CustomExtension => GetValue("CustomExtension"); + public static string PerceivedType => GetValue("PerceivedType"); + public static string DirectoryType => GetValue("DirectoryType"); public static string EnhanceMenu => GetValue("EnhanceMenu"); public static string ThirdRules => GetValue("ThirdRules"); public static string GuidBlocked => GetValue("GuidBlocked"); + public static string DragDrop => GetValue("DragDrop"); public static string PublicReferences => GetValue("PublicReferences"); + public static string IEMenu => GetValue("IEMenu"); } - /// 菜单 + /// 程序内右键菜单 public static class Menu { private static string GetValue(string key) => GetStringValue("Menu", key); @@ -132,9 +124,11 @@ public static class Menu public static string OnlyInExplorer => GetValue("OnlyInExplorer"); public static string NoWorkingDirectory => GetValue("NoWorkingDirectory"); public static string NeverDefault => GetValue("NeverDefault"); + public static string ShowAsDisabledIfHidden => GetValue("ShowAsDisabledIfHidden"); public static string Details => GetValue("Details"); public static string WebSearch => GetValue("WebSearch"); public static string ChangeCommand => GetValue("ChangeCommand"); + public static string RunAsAdministrator => GetValue("RunAsAdministrator"); public static string FileProperties => GetValue("FileProperties"); public static string FileLocation => GetValue("FileLocation"); public static string RegistryLocation => GetValue("RegistryLocation"); @@ -146,6 +140,9 @@ public static class Menu public static string BlockGuid => GetValue("BlockGuid"); public static string AddGuidDic => GetValue("AddGuidDic"); public static string InitialData => GetValue("InitialData"); + public static string BeforeSeparator => GetValue("BeforeSeparator"); + public static string ChangeGroup => GetValue("ChangeGroup"); + public static string RestoreDefault => GetValue("RestoreDefault"); public static string Edit => GetValue("Edit"); public static string Save => GetValue("Save"); } @@ -172,12 +169,19 @@ public static class Item public static string ShareWithSkype => GetValue("ShareWithSkype"); public static string NewItem => GetValue("NewItem"); public static string AddGuidBlockedItem => GetValue("AddGuidBlockedItem"); + public static string SelectExtension => GetValue("SelectExtension"); + public static string SelectPerceivedType => GetValue("SelectPerceivedType"); + public static string SelectDirectoryType => GetValue("SelectDirectoryType"); public static string CurrentExtension => GetValue("CurrentExtension"); - public static string SetPerceivedType => GetValue("SetPerceivedType"); + public static string CurrentPerceivedType => GetValue("CurrentPerceivedType"); + public static string CurrentDirectoryType => GetValue("CurrentDirectoryType"); public static string EditSubItems => GetValue("EditSubItems"); public static string InvalidItem => GetValue("InvalidItem"); public static string Separator => GetValue("Separator"); public static string LockNewMenu => GetValue("LockNewMenu"); + public static string WinXSortable => GetValue("WinXSortable"); + public static string ShowFilePath => GetValue("ShowFilePath"); + public static string OpenMoreRegedit => GetValue("OpenMoreRegedit"); } public static class Dialog @@ -187,9 +191,6 @@ public static class Dialog public static string Cancel => GetValue("Cancel"); public static string Browse => GetValue("Browse"); public static string Program => GetValue("Program"); - public static string NewShellItem => GetValue("NewShellItem"); - public static string NewSendToItem => GetValue("NewSendToItem"); - public static string NewOpenWithItem => GetValue("NewOpenWithItem"); public static string ItemText => GetValue("ItemText"); public static string ItemCommand => GetValue("ItemCommand"); public static string CommandArguments => GetValue("CommandArguments"); @@ -199,14 +200,26 @@ public static class Dialog public static string MultiMenu => GetValue("MultiMenu"); public static string Public => GetValue("Public"); public static string Private => GetValue("Private"); + public static string Administrator => GetValue("Administrator"); public static string InputGuid => GetValue("InputGuid"); public static string AddGuidDic => GetValue("AddGuidDic"); public static string DeleteGuidDic => GetValue("DeleteGuidDic"); - public static string SelectExtension => GetValue("SelectExtension"); + public static string TextFile => GetValue("TextFile"); + public static string DocumentFile => GetValue("DocumentFile"); + public static string ImageFile => GetValue("ImageFile"); + public static string VideoFile => GetValue("VideoFile"); + public static string AudioFile => GetValue("AudioFile"); + public static string CompressedFile => GetValue("CompressedFile"); + public static string SystemFile => GetValue("SystemFile"); + public static string DocumentDirectory => GetValue("DocumentDirectory"); + public static string ImageDirectory => GetValue("ImageDirectory"); + public static string VideoDirectory => GetValue("VideoDirectory"); + public static string AudioDirectory => GetValue("AudioDirectory"); public static string CheckReference => GetValue("CheckReference"); public static string CheckCopy => GetValue("CheckCopy"); public static string SelectSubMenuMode => GetValue("SelectSubMenuMode"); public static string RegistryFile => GetValue("RegistryFile"); + public static string SelectGroup => GetValue("SelectGroup"); } /// 消息框 @@ -218,6 +231,7 @@ public static class MessageBox public static string StringParsingFailed => GetValue("StringParsingFailed"); public static string TextLengthCannotExceed80 => GetValue("TextLengthCannotExceed80"); public static string ConfirmDeletePermanently => GetValue("ConfirmDeletePermanently"); + public static string DeleteButCanRestore => GetValue("DeleteButCanRestore"); public static string ConfirmDeleteReference => GetValue("ConfirmDeleteReference"); public static string ConfirmDelete => GetValue("ConfirmDelete"); public static string ConfirmDeleteReferenced => GetValue("ConfirmDeleteReferenced"); @@ -238,6 +252,9 @@ public static class MessageBox public static string FolderNotExists => GetValue("FolderNotExists"); public static string NoUpdateDetected => GetValue("NoUpdateDetected"); public static string AuthorityProtection => GetValue("AuthorityProtection"); + public static string WinXSorted => GetValue("WinXSorted"); + public static string RestoreDefault => GetValue("RestoreDefault"); + public static string DeleteGroup => GetValue("DeleteGroup"); } /// 其他文本 @@ -257,9 +274,9 @@ public static class Other public static string Dictionaries => GetValue("Dictionaries"); public static string Donate => GetValue("Donate"); public static string DonationList => GetValue("DonationList"); - public static string ConfigFile => GetValue("ConfigFile"); - public static string SaveToAppData => GetValue("SaveToAppData"); - public static string SaveToAppDir => GetValue("SaveToAppDir"); + public static string ConfigPath => GetValue("ConfigPath"); + public static string AppDataDir => GetValue("AppDataDir"); + public static string AppDir => GetValue("AppDir"); public static string OpenConfigDir => GetValue("OpenConfigDir"); public static string AutoBackup => GetValue("AutoBackup"); public static string OpenBackupDir => GetValue("OpenBackupDir"); @@ -282,7 +299,6 @@ public static class Tip public static string EditSubItems => GetValue("EditSubItems"); public static string InvalidItem => GetValue("InvalidItem"); public static string AddSeparator => GetValue("AddSeparator"); - public static string Separator => GetValue("Separator"); public static string AddReference => GetValue("AddReference"); public static string AddFromParentMenu => GetValue("AddFromParentMenu"); public static string DeleteGuidDic => GetValue("DeleteGuidDic"); @@ -291,7 +307,9 @@ public static class Tip public static string LastCheckUpdateTime => GetValue("LastCheckUpdateTime"); public static string OpenLanguagesDir => GetValue("OpenLanguagesDir"); public static string OpenDictionariesDir => GetValue("OpenDictionariesDir"); - public static string ShareWithSkype => GetValue("ShareWithSkype"); + public static string ConfigPath => GetValue("ConfigPath"); + public static string CommandFiles => GetValue("CommandFiles"); + public static string CreateGroup => GetValue("CreateGroup"); } } } \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Controls/IconDialog.cs b/ContextMenuManager/BluePointLilac.Controls/IconDialog.cs similarity index 96% rename from ContextMenuManager/BulePointLilac.Controls/IconDialog.cs rename to ContextMenuManager/BluePointLilac.Controls/IconDialog.cs index 6f9220f..84db31b 100644 --- a/ContextMenuManager/BulePointLilac.Controls/IconDialog.cs +++ b/ContextMenuManager/BluePointLilac.Controls/IconDialog.cs @@ -3,7 +3,7 @@ using System.Text; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { public sealed class IconDialog : CommonDialog { diff --git a/ContextMenuManager/BulePointLilac.Controls/InputDialog.cs b/ContextMenuManager/BluePointLilac.Controls/InputDialog.cs similarity index 97% rename from ContextMenuManager/BulePointLilac.Controls/InputDialog.cs rename to ContextMenuManager/BluePointLilac.Controls/InputDialog.cs index 155dce8..94cb116 100644 --- a/ContextMenuManager/BulePointLilac.Controls/InputDialog.cs +++ b/ContextMenuManager/BluePointLilac.Controls/InputDialog.cs @@ -1,10 +1,10 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using ContextMenuManager; using System; using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { public sealed class InputDialog : CommonDialog { diff --git a/ContextMenuManager/BulePointLilac.Controls/MyCheckBox.cs b/ContextMenuManager/BluePointLilac.Controls/MyCheckBox.cs similarity index 88% rename from ContextMenuManager/BulePointLilac.Controls/MyCheckBox.cs rename to ContextMenuManager/BluePointLilac.Controls/MyCheckBox.cs index a2d7b6f..23206b0 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MyCheckBox.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyCheckBox.cs @@ -1,7 +1,7 @@ using ContextMenuManager; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { public class MyCheckBox : PictureBox { @@ -18,6 +18,7 @@ public bool Checked public MyCheckBox() { + this.Checked = false; this.Cursor = Cursors.Hand; this.SizeMode = PictureBoxSizeMode.AutoSize; } diff --git a/ContextMenuManager/BulePointLilac.Controls/MyListBox.cs b/ContextMenuManager/BluePointLilac.Controls/MyListBox.cs similarity index 97% rename from ContextMenuManager/BulePointLilac.Controls/MyListBox.cs rename to ContextMenuManager/BluePointLilac.Controls/MyListBox.cs index 7486d55..6f6d7c7 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MyListBox.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyListBox.cs @@ -1,10 +1,10 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { public class MyListBox : Panel { @@ -58,12 +58,14 @@ public MyListItem HoveredItem value.ForeColor = Color.FromArgb(0, 138, 217); //value.BackColor = Color.FromArgb(200, 230, 250); value.Focus(); + HoveredItemChanged?.Invoke(null, null); } } + public event EventHandler HoveredItemChanged; + public void AddItem(MyListItem item) { - if(item.Parent == this) return; item.Parent = this; item.Width = Owner.Width - item.Margin.Horizontal; Owner.Resize += (sender, e) => item.Width = Owner.Width - item.Margin.Horizontal; @@ -136,7 +138,6 @@ public MyListItem() this.Margin = new Padding(0); this.ForeColor = Color.FromArgb(80, 80, 80); this.BackColor = Color.FromArgb(250, 250, 250); - this.BackgroundImageLayout = ImageLayout.Stretch; this.Font = SystemFonts.IconTitleFont; this.Controls.AddRange(new Control[] { lblSeparator, flpControls, lblText, picImage }); flpControls.MouseEnter += (sender, e) => this.OnMouseEnter(null); @@ -204,7 +205,7 @@ public bool HasImage }; private readonly Label lblSeparator = new Label { - BackColor = Color.FromArgb(200, 200, 200), + BackColor = Color.FromArgb(220, 220, 220), Dock = DockStyle.Bottom, Height = 1 }; diff --git a/ContextMenuManager/BulePointLilac.Controls/MyMainForm.cs b/ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs similarity index 94% rename from ContextMenuManager/BulePointLilac.Controls/MyMainForm.cs rename to ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs index 6f01727..6778cc3 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MyMainForm.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs @@ -1,9 +1,9 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { public class MyMainForm : Form { diff --git a/ContextMenuManager/BulePointLilac.Controls/MySideBar.cs b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs similarity index 95% rename from ContextMenuManager/BulePointLilac.Controls/MySideBar.cs rename to ContextMenuManager/BluePointLilac.Controls/MySideBar.cs index 1a5dd2f..a57994c 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MySideBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MySideBar.cs @@ -1,11 +1,11 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { - public class MySideBar : Panel + public sealed class MySideBar : Panel { public MySideBar() { @@ -92,7 +92,7 @@ public Color HoveredForeColor }; readonly Label LblSeparator = new Label { - BackColor = Color.FromArgb(200, 200, 200), + BackColor = Color.FromArgb(220, 220, 220), Dock = DockStyle.Right, Width = 1, }; @@ -106,7 +106,7 @@ public int GetItemWidth(string str) /// 绘制所有项目作为底图 private void PaintItems() { - this.BackgroundImage = new Bitmap(Width, Height); + this.BackgroundImage = new Bitmap(Width, ItemHeight * ItemNames.Length); using(Graphics g = Graphics.FromImage(BackgroundImage)) { g.Clear(BackColor); @@ -132,6 +132,7 @@ private void PaintItems() /// 刷新选中的项目 private void RefreshItem(Panel panel, int index) { + panel.CreateGraphics().Clear(panel.BackColor); panel.Top = index < 0 ? -ItemHeight : (TopSpace + index * ItemHeight); panel.Text = index < 0 ? null : ItemNames[index]; panel.Refresh(); @@ -141,7 +142,6 @@ private void RefreshItem(Panel panel, int index) private void PaintItem(object sender, PaintEventArgs e) { Control ctr = (Control)sender; - e.Graphics.Clear(ctr.BackColor); e.Graphics.DrawString(ctr.Text, Font, new SolidBrush(ctr.ForeColor), new PointF(HorizontalSpace, VerticalSpace)); diff --git a/ContextMenuManager/BulePointLilac.Controls/MyStatusBar.cs b/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs similarity index 68% rename from ContextMenuManager/BulePointLilac.Controls/MyStatusBar.cs rename to ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs index 2e10f60..986ef76 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MyStatusBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs @@ -1,11 +1,11 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { - public class MyStatusBar : Panel + public sealed class MyStatusBar : Panel { public static readonly string DefaultText = $"Ver: {new Version(Application.ProductVersion).ToString(2)} {Application.CompanyName}"; @@ -22,17 +22,29 @@ public MyStatusBar() public new string Text { get => base.Text; - set { base.Text = value; Refresh(); } + set + { + if(base.Text == value) return; + base.Text = value; Refresh(); + } } public new Font Font { get => base.Font; - set { base.Font = value; Refresh(); } + set + { + if(base.Font == value) return; + base.Font = value; Refresh(); + } } public new Color ForeColor { get => base.ForeColor; - set { base.ForeColor = value; Refresh(); } + set + { + if(base.ForeColor == value) return; + base.ForeColor = value; Refresh(); + } } protected override void OnPaint(PaintEventArgs e) diff --git a/ContextMenuManager/BulePointLilac.Controls/MyToolBar.cs b/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs similarity index 86% rename from ContextMenuManager/BulePointLilac.Controls/MyToolBar.cs rename to ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs index 9dfe810..56db5a1 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MyToolBar.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs @@ -1,11 +1,11 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { - public class MyToolBar : FlowLayoutPanel + public sealed class MyToolBar : FlowLayoutPanel { public MyToolBar() { @@ -43,7 +43,7 @@ public void AddButton(MyToolBarButton button) button.Margin = new Padding(12.DpiZoom(), 4.DpiZoom(), 0, 0); button.MouseDown += (sender, e) => { - if(e.Button == MouseButtons.Left) { SelectedButton = button; button.Cursor = Cursors.Default; } + if(button.CanBeSelected) { SelectedButton = button; button.Cursor = Cursors.Default; } }; button.MouseEnter += (sender, e) => { @@ -61,7 +61,7 @@ public void AddButtons(MyToolBarButton[] buttons) } } - public class MyToolBarButton : Panel + public sealed class MyToolBarButton : Panel { public MyToolBarButton(Image image, string text) { @@ -73,6 +73,7 @@ public MyToolBarButton(Image image, string text) lblText.Resize += (sender, e) => lblText.Left = (Width - lblText.Width) / 2; this.Image = image; this.Text = text; + this.CanBeSelected = true; MyToolTip.SetToolTip(this, text); lblText.SetEnabled(false); } @@ -108,5 +109,11 @@ public float Opacity get => BackColor.A / 255; set => BackColor = Color.FromArgb((int)(value * 255), Color.White); } + public bool CanBeSelected { get; set; } + + protected override void OnMouseDown(MouseEventArgs e) + { + if(e.Button == MouseButtons.Left) base.OnMouseDown(e); + } } } \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Controls/MyToolTip.cs b/ContextMenuManager/BluePointLilac.Controls/MyToolTip.cs similarity index 84% rename from ContextMenuManager/BulePointLilac.Controls/MyToolTip.cs rename to ContextMenuManager/BluePointLilac.Controls/MyToolTip.cs index 0317f0d..a45d3e5 100644 --- a/ContextMenuManager/BulePointLilac.Controls/MyToolTip.cs +++ b/ContextMenuManager/BluePointLilac.Controls/MyToolTip.cs @@ -1,9 +1,9 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { - public sealed class MyToolTip + public static class MyToolTip { public static void SetToolTip(Control ctr, string tip) { diff --git a/ContextMenuManager/BulePointLilac.Controls/PictureButton.cs b/ContextMenuManager/BluePointLilac.Controls/PictureButton.cs similarity index 96% rename from ContextMenuManager/BulePointLilac.Controls/PictureButton.cs rename to ContextMenuManager/BluePointLilac.Controls/PictureButton.cs index 6ef5453..578c9c3 100644 --- a/ContextMenuManager/BulePointLilac.Controls/PictureButton.cs +++ b/ContextMenuManager/BluePointLilac.Controls/PictureButton.cs @@ -2,7 +2,7 @@ using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { public class PictureButton : PictureBox { diff --git a/ContextMenuManager/BulePointLilac.Controls/ReadOnlyRichTextBox.cs b/ContextMenuManager/BluePointLilac.Controls/ReadOnlyRichTextBox.cs similarity index 78% rename from ContextMenuManager/BulePointLilac.Controls/ReadOnlyRichTextBox.cs rename to ContextMenuManager/BluePointLilac.Controls/ReadOnlyRichTextBox.cs index a914af4..0cb73af 100644 --- a/ContextMenuManager/BulePointLilac.Controls/ReadOnlyRichTextBox.cs +++ b/ContextMenuManager/BluePointLilac.Controls/ReadOnlyRichTextBox.cs @@ -1,10 +1,10 @@ -using System.Diagnostics; -using System.Drawing; +using System.Drawing; using System.Windows.Forms; +using BluePointLilac.Methods; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { - sealed class ReadOnlyRichTextBox : RichTextBox + public sealed class ReadOnlyRichTextBox : RichTextBox { public ReadOnlyRichTextBox() { @@ -30,7 +30,7 @@ protected override void WndProc(ref Message m) protected override void OnLinkClicked(LinkClickedEventArgs e) { - base.OnLinkClicked(e); Process.Start(e.LinkText); + base.OnLinkClicked(e); ExternalProgram.OpenUrl(e.LinkText); } } } \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Controls/ResizbleForm.cs b/ContextMenuManager/BluePointLilac.Controls/ResizbleForm.cs similarity index 97% rename from ContextMenuManager/BulePointLilac.Controls/ResizbleForm.cs rename to ContextMenuManager/BluePointLilac.Controls/ResizbleForm.cs index f705217..f1dbb39 100644 --- a/ContextMenuManager/BulePointLilac.Controls/ResizbleForm.cs +++ b/ContextMenuManager/BluePointLilac.Controls/ResizbleForm.cs @@ -1,10 +1,10 @@ using System; using System.Windows.Forms; -namespace BulePointLilac.Controls +namespace BluePointLilac.Controls { /// 限制水平、竖直方向调整大小的窗体 - class ResizbleForm : Form + public class ResizbleForm : Form { /// 水平方向可调整大小 public bool HorizontalResizable { get; set; } = true; diff --git a/ContextMenuManager/BulePointLilac.Methods/ControlExtension.cs b/ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs similarity index 98% rename from ContextMenuManager/BulePointLilac.Methods/ControlExtension.cs rename to ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs index ab5c756..04ad777 100644 --- a/ContextMenuManager/BulePointLilac.Methods/ControlExtension.cs +++ b/ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; using System.Windows.Forms; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class ControlExtension { diff --git a/ContextMenuManager/BluePointLilac.Methods/DesktopIni.cs b/ContextMenuManager/BluePointLilac.Methods/DesktopIni.cs new file mode 100644 index 0000000..0c0c78b --- /dev/null +++ b/ContextMenuManager/BluePointLilac.Methods/DesktopIni.cs @@ -0,0 +1,34 @@ +using System.IO; + +namespace BluePointLilac.Methods +{ + public static class DesktopIni + { + private const string LocalizedFileNames = "LocalizedFileNames"; + + private static string GetIniPath(string filePath) => $@"{Path.GetDirectoryName(filePath)}\desktop.ini"; + + public static void DeleteLocalizedFileNames(string filePath) + { + IniWriter writer = new IniWriter(GetIniPath(filePath)); + string fileName = Path.GetFileName(filePath); + writer.DeleteKey(LocalizedFileNames, fileName); + } + + public static void SetLocalizedFileNames(string filePath, string name) + { + IniWriter writer = new IniWriter(GetIniPath(filePath)); + string fileName = Path.GetFileName(filePath); + writer.SetValue(LocalizedFileNames, fileName, name); + } + + public static string GetLocalizedFileNames(string filePath, bool translate = false) + { + IniWriter writer = new IniWriter(GetIniPath(filePath)); + string fileName = Path.GetFileName(filePath); + string name = writer.GetValue(LocalizedFileNames, fileName); + if(translate) name = ResourceString.GetDirectString(name); + return name; + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/DirectoryEx.cs b/ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs similarity index 95% rename from ContextMenuManager/BulePointLilac.Methods/DirectoryEx.cs rename to ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs index 02ca7e9..3903c8d 100644 --- a/ContextMenuManager/BulePointLilac.Methods/DirectoryEx.cs +++ b/ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs @@ -1,6 +1,6 @@ using System.IO; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class DirectoryEx { diff --git a/ContextMenuManager/BulePointLilac.Methods/EncodingType.cs b/ContextMenuManager/BluePointLilac.Methods/EncodingType.cs similarity index 81% rename from ContextMenuManager/BulePointLilac.Methods/EncodingType.cs rename to ContextMenuManager/BluePointLilac.Methods/EncodingType.cs index b5564a0..46d7eb3 100644 --- a/ContextMenuManager/BulePointLilac.Methods/EncodingType.cs +++ b/ContextMenuManager/BluePointLilac.Methods/EncodingType.cs @@ -1,9 +1,8 @@ using System; using System.IO; -using System.Linq; using System.Text; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { /// 获取文本文件编码类型 /// 代码主要为转载,仅做简单改动 @@ -20,10 +19,6 @@ public static Encoding GetType(string filePath) return GetType(fs); } - static readonly byte[] Unicode = { 0xFF, 0xFE, 0x41 }; - static readonly byte[] UnicodeBIG = { 0xFE, 0xFF, 0x00 }; - static readonly byte[] UTF8 = { 0xEF, 0xBB, 0xBF }; //带BOM - /// 通过给定的文件流,判断文件的编码类型 /// 文件流 /// 文件的编码类型 @@ -33,11 +28,11 @@ public static Encoding GetType(FileStream fs) int.TryParse(fs.Length.ToString(), out int i); using(BinaryReader reader = new BinaryReader(fs, Encoding.Default)) ss = reader.ReadBytes(i); - byte[] rs = ss.Take(3).ToArray(); - if((rs == UTF8) || IsUTF8Bytes(ss)) return Encoding.UTF8; - else if(rs == UnicodeBIG) return Encoding.BigEndianUnicode; - else if(rs == Unicode) return Encoding.Unicode; - else return Encoding.Default; + if(IsUTF8Bytes(ss)) return Encoding.UTF8; + if(ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF) return Encoding.UTF8;//带BOM + if(ss[0] == 0xFE && ss[1] == 0xFF) return Encoding.BigEndianUnicode; //UTF-16BE + if(ss[0] == 0xFF && ss[1] == 0xFE) return Encoding.Unicode; //UTF-16LE + return Encoding.Default; } /// 判断是否是不带 BOM 的 UTF8 格式 diff --git a/ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs b/ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs new file mode 100644 index 0000000..c63a836 --- /dev/null +++ b/ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs @@ -0,0 +1,177 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace BluePointLilac.Methods +{ + public static class ExternalProgram + { + public static void JumpRegEdit(string regPath, string valueName = null, bool moreOpen = false) + { + IntPtr hMain = FindWindow("RegEdit_RegEdit", null); + if(moreOpen || hMain == IntPtr.Zero) + { + using(Process process = Process.Start("regedit.exe", "-m")) + { + process.WaitForInputIdle(); + hMain = process.MainWindowHandle; + } + } + + IntPtr hTree = FindWindowEx(hMain, IntPtr.Zero, "SysTreeView32", null); + IntPtr hList = FindWindowEx(hMain, IntPtr.Zero, "SysListView32", null); + + SetForegroundWindow(hTree); + SetFocus(hTree); + SendMessage(hTree, WM_KEYDOWN, VK_HOME, null); + Thread.Sleep(50); + SendMessage(hTree, WM_KEYDOWN, VK_RIGHT, null); + foreach(char chr in Encoding.Default.GetBytes(regPath)) + { + if(chr == '\\') + { + Thread.Sleep(50); + SendMessage(hTree, WM_KEYDOWN, VK_RIGHT, null); + } + else SendMessage(hTree, WM_CHAR, Convert.ToInt16(chr), null); + } + + if(!string.IsNullOrEmpty(valueName)) + { + Thread.Sleep(50); + SetForegroundWindow(hList); + SetFocus(hList); + SendMessage(hList, WM_KEYDOWN, VK_HOME, null); + foreach(char chr in Encoding.Default.GetBytes(valueName)) + { + SendMessage(hList, WM_CHAR, Convert.ToInt16(chr), null); + } + } + } + + public static void JumpExplorer(string filePath) + { + using(Process process = new Process()) + { + if(File.Exists(filePath)) + { + process.StartInfo.FileName = "explorer.exe"; + process.StartInfo.Arguments = $"/select,{filePath}"; + process.Start(); + } + else if(Directory.Exists(filePath)) + { + process.StartInfo.FileName = filePath; + process.Start(); + } + } + } + + public static bool ShowPropertiesDialog(string filePath) + { + SHELLEXECUTEINFO info = new SHELLEXECUTEINFO + { + lpVerb = "Properties", + //lpParameters = "详细信息";//显示选项卡,此处有语言差异 + lpFile = filePath, + nShow = SW_SHOW, + fMask = SEE_MASK_INVOKEIDLIST, + cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)) + }; + return ShellExecuteEx(ref info); + } + + public static void RestartExplorer() + { + using(Process process = new Process()) + { + process.StartInfo = new ProcessStartInfo + { + FileName = "taskkill.exe", + Arguments = "-f -im explorer.exe", + WindowStyle = ProcessWindowStyle.Hidden + }; + process.Start(); + process.WaitForExit(); + process.StartInfo = new ProcessStartInfo("explorer.exe"); + process.Start(); + } + } + + public static void OpenUrl(string url) + { + //替换网址转义符 + url = url.Replace("%", "%25").Replace("#", "%23").Replace("&", "%26").Replace("+", "%2B"); + using(Process process = new Process()) + { + //通过explorer来调用浏览器打开链接,避免管理员权限影响 + process.StartInfo.FileName = "explorer.exe"; + process.StartInfo.Arguments = $"\"{url}\""; + process.Start(); + } + } + + public static void OpenNotepadWithText(string text) + { + using(Process process = Process.Start("notepad.exe")) + { + Thread.Sleep(200); + IntPtr handle = FindWindowEx(process.MainWindowHandle, IntPtr.Zero, "Edit", null); + SendMessage(handle, WM_SETTEXT, 0, text); + } + } + + private const int SW_SHOW = 5; + private const uint SEE_MASK_INVOKEIDLIST = 12; + private const int WM_SETTEXT = 0xC; + private const int WM_KEYDOWN = 0x100; + private const int WM_CHAR = 0x102; + private const int VK_HOME = 0x24; + private const int VK_RIGHT = 0x27; + + [DllImport("User32.dll")] + private static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("User32.dll")] + private static extern bool SetFocus(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr FindWindow(string lpszClass, string lpszWindow); + + [DllImport("user32.dll")] + private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChild, string lpszClass, string lpszWindow); + + [DllImport("user32.dll")] + private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam); + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + private static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct SHELLEXECUTEINFO + { + public int cbSize; + public uint fMask; + public IntPtr hwnd; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpVerb; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpFile; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpParameters; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpDirectory; + public int nShow; + public IntPtr hInstApp; + public IntPtr lpIDList; + [MarshalAs(UnmanagedType.LPTStr)] + public string lpClass; + public IntPtr hkeyClass; + public uint dwHotKey; + public IntPtr hIcon; + public IntPtr hProcess; + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/FileExtension.cs b/ContextMenuManager/BluePointLilac.Methods/FileExtension.cs similarity index 94% rename from ContextMenuManager/BulePointLilac.Methods/FileExtension.cs rename to ContextMenuManager/BluePointLilac.Methods/FileExtension.cs index fef31a7..962b3cf 100644 --- a/ContextMenuManager/BulePointLilac.Methods/FileExtension.cs +++ b/ContextMenuManager/BluePointLilac.Methods/FileExtension.cs @@ -1,6 +1,6 @@ using Microsoft.Win32; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class FileExtension { diff --git a/ContextMenuManager/BulePointLilac.Methods/GuidEx.cs b/ContextMenuManager/BluePointLilac.Methods/GuidEx.cs similarity index 54% rename from ContextMenuManager/BulePointLilac.Methods/GuidEx.cs rename to ContextMenuManager/BluePointLilac.Methods/GuidEx.cs index 619c488..f4fb2fa 100644 --- a/ContextMenuManager/BulePointLilac.Methods/GuidEx.cs +++ b/ContextMenuManager/BluePointLilac.Methods/GuidEx.cs @@ -1,22 +1,30 @@ using System; +using System.Text.RegularExpressions; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { //为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的Guid.TryParse)写了这个扩展方法 public static class GuidEx { public static bool TryParse(string str, out Guid guid) { - try + if(IsGuid(str)) { guid = new Guid(str); return true; } - catch + else { guid = Guid.Empty; return false; } } + + public static bool IsGuid(string str) + { + if(string.IsNullOrEmpty(str)) return false; + Regex guidRegEx = new Regex(@"[a-fA-F0-9]{8}(\-[a-fA-F0-9]{4}){3}\-[a-fA-F0-9]{12}"); + return guidRegEx.IsMatch(str); + } } } \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/HighDpi.cs b/ContextMenuManager/BluePointLilac.Methods/HighDpi.cs similarity index 97% rename from ContextMenuManager/BulePointLilac.Methods/HighDpi.cs rename to ContextMenuManager/BluePointLilac.Methods/HighDpi.cs index 70ba625..085b07d 100644 --- a/ContextMenuManager/BulePointLilac.Methods/HighDpi.cs +++ b/ContextMenuManager/BluePointLilac.Methods/HighDpi.cs @@ -2,7 +2,7 @@ using System.Windows; using System.Windows.Forms; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class HighDpi { diff --git a/ContextMenuManager/BulePointLilac.Methods/ImageExtension.cs b/ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs similarity index 98% rename from ContextMenuManager/BulePointLilac.Methods/ImageExtension.cs rename to ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs index 75da33d..f7f0074 100644 --- a/ContextMenuManager/BulePointLilac.Methods/ImageExtension.cs +++ b/ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs @@ -2,7 +2,7 @@ using System.Drawing.Drawing2D; using System.Drawing.Imaging; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class ImageExtension { diff --git a/ContextMenuManager/BulePointLilac.Methods/IniReader.cs b/ContextMenuManager/BluePointLilac.Methods/IniReader.cs similarity index 97% rename from ContextMenuManager/BulePointLilac.Methods/IniReader.cs rename to ContextMenuManager/BluePointLilac.Methods/IniReader.cs index db327f5..1a22939 100644 --- a/ContextMenuManager/BulePointLilac.Methods/IniReader.cs +++ b/ContextMenuManager/BluePointLilac.Methods/IniReader.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public sealed class IniReader { @@ -65,6 +65,7 @@ private void ReadLines(List lines) int k = lines[j].IndexOf('='); string key = lines[j].Substring(0, k).TrimEnd(); string value = lines[j].Substring(k + 1).TrimStart(); + if(keyValues.ContainsKey(key)) continue; keyValues.Add(key, value); } } diff --git a/ContextMenuManager/BulePointLilac.Methods/IniWriter.cs b/ContextMenuManager/BluePointLilac.Methods/IniWriter.cs similarity index 97% rename from ContextMenuManager/BulePointLilac.Methods/IniWriter.cs rename to ContextMenuManager/BluePointLilac.Methods/IniWriter.cs index 704117f..7a79dcf 100644 --- a/ContextMenuManager/BulePointLilac.Methods/IniWriter.cs +++ b/ContextMenuManager/BluePointLilac.Methods/IniWriter.cs @@ -3,7 +3,7 @@ using System.IO; using System.Text; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public sealed class IniWriter { @@ -16,7 +16,7 @@ public IniWriter(string filePath) } public string FilePath { get; set; } - public Encoding Encoding { get; set; } = Encoding.UTF8; + public Encoding Encoding { get; set; } = Encoding.Unicode; public bool DeleteFileWhenEmpty { get; set; } private List GetLines() @@ -85,7 +85,7 @@ private void SetValue(string section, string key, ref string value, bool isGetVa { if(key != null && value != null) { - if(lines.Count > 0) lines.Add(string.Empty);//添加空行 + lines.Add(string.Empty);//添加空行 //目标section不存在则添加到最后 lines.Add(sectionLine); lines.Add(keyLine); diff --git a/ContextMenuManager/BulePointLilac.Methods/MessageBoxEx.cs b/ContextMenuManager/BluePointLilac.Methods/MessageBoxEx.cs similarity index 92% rename from ContextMenuManager/BulePointLilac.Methods/MessageBoxEx.cs rename to ContextMenuManager/BluePointLilac.Methods/MessageBoxEx.cs index 2adcff6..42aa8c3 100644 --- a/ContextMenuManager/BulePointLilac.Methods/MessageBoxEx.cs +++ b/ContextMenuManager/BluePointLilac.Methods/MessageBoxEx.cs @@ -1,7 +1,7 @@ using ContextMenuManager; using System.Windows.Forms; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class MessageBoxEx { diff --git a/ContextMenuManager/BulePointLilac.Methods/ObjectPath.cs b/ContextMenuManager/BluePointLilac.Methods/ObjectPath.cs similarity index 63% rename from ContextMenuManager/BulePointLilac.Methods/ObjectPath.cs rename to ContextMenuManager/BluePointLilac.Methods/ObjectPath.cs index 281562a..9d24bc9 100644 --- a/ContextMenuManager/BulePointLilac.Methods/ObjectPath.cs +++ b/ContextMenuManager/BluePointLilac.Methods/ObjectPath.cs @@ -1,20 +1,18 @@ using Microsoft.Win32; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { - public class ObjectPath + public static class ObjectPath { /// 路径类型 public enum PathType { File, Directory, Registry } - //右键菜单仅支持%SystemRoot%\System32和%SystemRoot%两个环境变量,不考虑其他系统环境变量和用户环境变量,和Win+R命令有区别 - private static readonly string[] EnvironmentDirectorys = { @"%SystemRoot%\System32", @"%SystemRoot%" }; private const string RegAppPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; private const string RegLastPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit"; + private const string ShellExecuteCommand = "mshta vbscript:createobject(\"shell.application\").shellexecute(\""; private static readonly char[] IllegalChars = { '/', '*', '?', '\"', '<', '>', '|' }; private static readonly List IgnoreCommandParts = new List { "", "%1", "%v" }; @@ -22,26 +20,28 @@ public enum PathType { File, Directory, Registry } /// 根据文件名获取完整的文件路径 /// fileName为Win+R、注册表等可直接使用的文件名 /// 文件名 - /// 成功提取返回现有文件路径,否则返回值为null + /// 成功提取返回true, fullPath为现有文件路径; 否则返回false, fullPath为null public static bool GetFullFilePath(string fileName, out string fullPath) { fullPath = null; - if(File.Exists(fileName)) - { - fullPath = fileName; - return true; - } + if(fileName.IsNullOrWhiteSpace()) return false; + foreach(string name in new[] { fileName, $"{fileName}.exe" }) { - foreach(string dir in EnvironmentDirectorys) + //右键菜单仅支持%SystemRoot%\System32和%SystemRoot%两个环境变量,不考虑其他系统环境变量和用户环境变量,和Win+R命令有区别 + foreach(string dir in new[] { "", @"%SystemRoot%\System32\", @"%SystemRoot%\" }) { - fullPath = Environment.ExpandEnvironmentVariables($@"{dir}\{name}"); + fullPath = Environment.ExpandEnvironmentVariables($@"{dir}{name}"); if(File.Exists(fullPath)) return true; } - fullPath = Registry.GetValue($@"{RegAppPath}\{name}", "", null)?.ToString(); - if(File.Exists(fullPath)) return true; + if(!name.Contains("\\")) + { + fullPath = Registry.GetValue($@"{RegAppPath}\{name}", "", null)?.ToString(); + if(File.Exists(fullPath)) return true; + } } + fullPath = null; return false; } @@ -54,27 +54,54 @@ public static string ExtractFilePath(string command) command = Environment.ExpandEnvironmentVariables(command).Replace(@"\\", @"\"); if(File.Exists(command)) return command; + if(command.StartsWith(ShellExecuteCommand, StringComparison.OrdinalIgnoreCase)) + { + command = command.Remove(0, ShellExecuteCommand.Length); + string[] arr = command.Split(new[] { "\",\"" }, StringSplitOptions.None); + if(arr.Length > 0) + { + string filePath = null; + string fileName = arr[0]; + if(arr.Length > 1) + { + string arguments = arr[1]; + filePath = ExtractFilePath(arguments); + if(filePath != null) return filePath; + } + if(GetFullFilePath(fileName, out filePath)) return filePath; + } + } + string[] strs = Array.FindAll(command.Split(IllegalChars), str => IgnoreCommandParts.Any(part => !part.Equals(str.Trim()))).Reverse().ToArray(); - foreach(string str in strs) + foreach(string str1 in strs) { - int index = str.Length; + string str2 = str1; + int index = -1; do { - string path1 = str.Substring(0, index); - List paths = new List { path1 }; - if(path1.Contains(",")) paths.Add(path1.Substring(0, path1.LastIndexOf(','))); + List paths = new List(); + string path1 = str2.Substring(index + 1); + paths.Add(path1); + if(path1.Contains(",")) paths.AddRange(path1.Split(',')); + if(index > 0) + { + string path2 = str2.Substring(0, index); + paths.Add(path2); + if(path2.Contains(",")) paths.AddRange(path2.Split(',')); + } foreach(string path in paths) { if(GetFullFilePath(path, out string fullPath)) return fullPath; } - index = path1.LastIndexOf(' '); - } while(index != -1); + str2 = path1; + index = str2.IndexOf(' '); + } + while(index != -1); } return null; } - /// 移除文件或文件夹名称中的非法字符 /// 文件或文件夹名称 /// 返回移除非法字符后的文件或文件夹名称 @@ -84,7 +111,6 @@ public static string RemoveIllegalChars(string fileName) return fileName.Replace("\\", "").Replace(":", ""); } - /// 判断文件或文件夹或注册表项是否存在 /// 文件或文件夹或注册表项路径 /// 路径类型 @@ -108,39 +134,21 @@ public static bool ObjectPathExist(string path, PathType type) /// 目标路径 /// 路径类型 /// 如果目标路径不存在则返回目标路径,否则返回带序号的新路径 - public static string GetNewPathWithIndex(string oldPath, PathType type) + public static string GetNewPathWithIndex(string oldPath, PathType type, int startIndex = -1) { string newPath; string dirPath = type == PathType.Registry ? RegistryEx.GetParentPath(oldPath) : Path.GetDirectoryName(oldPath); string name = type == PathType.Registry ? RegistryEx.GetKeyName(oldPath) : Path.GetFileNameWithoutExtension(oldPath); string extension = type == PathType.Registry ? "" : Path.GetExtension(oldPath); - int index = 0; do { newPath = $@"{dirPath}\{name}"; - if(index > 0) newPath += index; + if(startIndex > -1) newPath += startIndex; newPath += extension; - index++; + startIndex++; } while(ObjectPathExist(newPath, type)); return newPath; } - - public static void ShowPath(string path, PathType type) - { - switch(type) - { - case PathType.Directory: - Process.Start(path); - break; - case PathType.File: - Process.Start("explorer.exe", $" /select,{path}"); - break; - case PathType.Registry: - Registry.SetValue(RegLastPath, "LastKey", path); - Process.Start("regedit.exe", "-m"); - break; - } - } } } \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/RegTrustedInstaller.cs b/ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs similarity index 91% rename from ContextMenuManager/BulePointLilac.Methods/RegTrustedInstaller.cs rename to ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs index f72129b..9bc96ea 100644 --- a/ContextMenuManager/BulePointLilac.Methods/RegTrustedInstaller.cs +++ b/ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs @@ -5,7 +5,7 @@ using System.Security.AccessControl; using System.Security.Principal; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { /// 获取TrustedInstaller权限注册表项的所有权 /// 代码主要为转载,仅做简单改动 @@ -18,18 +18,21 @@ static class NativeMethod public const string TakeOwnership = "SeTakeOwnershipPrivilege"; public const string Restore = "SeRestorePrivilege"; + [StructLayout(LayoutKind.Sequential)] public struct LUID { public int lowPart; public int highPart; } + [StructLayout(LayoutKind.Sequential)] public struct LUID_AND_ATTRIBUTES { public LUID Luid; public int Attributes; } + [StructLayout(LayoutKind.Sequential)] public struct TOKEN_PRIVILEGES { public int PrivilegeCount; @@ -37,6 +40,7 @@ public struct TOKEN_PRIVILEGES public LUID_AND_ATTRIBUTES[] Privileges; } + [Flags] public enum PrivilegeAttributes { /// 特权被禁用. @@ -51,6 +55,7 @@ public enum PrivilegeAttributes UsedForAccess = -2147483648 } + [Flags] public enum TokenAccessRights { /// 向进程附加主令牌的权限. @@ -82,6 +87,7 @@ public enum TokenAccessRights Execute = AccessTypeMasks.StandardRightsExecute | Impersonate } + [Flags] private enum AccessTypeMasks { Delete = 65536, @@ -98,14 +104,17 @@ private enum AccessTypeMasks } [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool AdjustTokenPrivileges(IntPtr accessTokenHandle, bool disableAllPrivileges, - ref TOKEN_PRIVILEGES newState, int bufferLength, ref TOKEN_PRIVILEGES previousState, ref int returnLength); + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool AdjustTokenPrivileges([In] IntPtr accessTokenHandle, [In] bool disableAllPrivileges, + [In] ref TOKEN_PRIVILEGES newState, [In] int bufferLength, [In, Out] ref TOKEN_PRIVILEGES previousState, [In, Out] ref int returnLength); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - private static extern bool LookupPrivilegeValue(string systemName, string name, ref LUID luid); + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool LookupPrivilegeValue([In] string systemName, [In] string name, [In, Out] ref LUID luid); [DllImport("advapi32.dll", SetLastError = true)] - private static extern bool OpenProcessToken(IntPtr processHandle, TokenAccessRights desiredAccess, ref IntPtr tokenHandle); + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool OpenProcessToken([In] IntPtr processHandle, [In] TokenAccessRights desiredAccess, [In, Out] ref IntPtr tokenHandle); [DllImport("kernel32.dll", SetLastError = true)] private static extern int GetLastError(); diff --git a/ContextMenuManager/BulePointLilac.Methods/RegistryEx.cs b/ContextMenuManager/BluePointLilac.Methods/RegistryEx.cs similarity index 96% rename from ContextMenuManager/BulePointLilac.Methods/RegistryEx.cs rename to ContextMenuManager/BluePointLilac.Methods/RegistryEx.cs index ef2d2b7..13b249f 100644 --- a/ContextMenuManager/BulePointLilac.Methods/RegistryEx.cs +++ b/ContextMenuManager/BluePointLilac.Methods/RegistryEx.cs @@ -4,7 +4,7 @@ using System.IO; using System.Security.AccessControl; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class RegistryEx { @@ -46,7 +46,6 @@ public static void MoveTo(string srcPath, string dstPath) public static RegistryKey CreateSubKey(this RegistryKey key, string subKeyName, bool writable) { key.CreateSubKey(subKeyName).Close(); - RegTrustedInstaller.TakeRegTreeOwnerShip($@"{key.Name}\{subKeyName}"); return key.OpenSubKey(subKeyName, writable); } @@ -142,8 +141,8 @@ public static RegistryKey GetRegistryKey(string regPath, RegistryKeyPermissionCh public static void Export(string regPath, string filePath) { if(File.Exists(filePath)) File.Delete(filePath); - Process process = Process.Start("regedit.exe", $" /e \"{filePath}\" \"{regPath}\""); - process.WaitForExit(); + using(Process process = Process.Start("regedit.exe", $"/e \"{filePath}\" \"{regPath}\"")) + process.WaitForExit(); } } } \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/ResourceIcon.cs b/ContextMenuManager/BluePointLilac.Methods/ResourceIcon.cs similarity index 99% rename from ContextMenuManager/BulePointLilac.Methods/ResourceIcon.cs rename to ContextMenuManager/BluePointLilac.Methods/ResourceIcon.cs index 8037cf4..0a9f749 100644 --- a/ContextMenuManager/BulePointLilac.Methods/ResourceIcon.cs +++ b/ContextMenuManager/BluePointLilac.Methods/ResourceIcon.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Windows.Forms; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class ResourceIcon { diff --git a/ContextMenuManager/BulePointLilac.Methods/ResourceString.cs b/ContextMenuManager/BluePointLilac.Methods/ResourceString.cs similarity index 96% rename from ContextMenuManager/BulePointLilac.Methods/ResourceString.cs rename to ContextMenuManager/BluePointLilac.Methods/ResourceString.cs index 309e2d7..df6f664 100644 --- a/ContextMenuManager/BulePointLilac.Methods/ResourceString.cs +++ b/ContextMenuManager/BluePointLilac.Methods/ResourceString.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; using System.Text; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class ResourceString { diff --git a/ContextMenuManager/BulePointLilac.Methods/RichTextBoxExtension.cs b/ContextMenuManager/BluePointLilac.Methods/RichTextBoxExtension.cs similarity index 99% rename from ContextMenuManager/BulePointLilac.Methods/RichTextBoxExtension.cs rename to ContextMenuManager/BluePointLilac.Methods/RichTextBoxExtension.cs index ab16bdf..9436a3c 100644 --- a/ContextMenuManager/BulePointLilac.Methods/RichTextBoxExtension.cs +++ b/ContextMenuManager/BluePointLilac.Methods/RichTextBoxExtension.cs @@ -4,7 +4,7 @@ using System.Windows.Forms; using System.Xml.Linq; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class RichTextBoxExtension { diff --git a/ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs b/ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs new file mode 100644 index 0000000..01b541c --- /dev/null +++ b/ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace BluePointLilac.Methods +{ + public static class SingleInstance + { + public static bool IsRunning() + { + using(Process current = Process.GetCurrentProcess()) + { + string fileName = current.MainModule.FileName; + string processName = Path.GetFileNameWithoutExtension(fileName); + foreach(Process process in Process.GetProcessesByName(processName)) + { + using(process) + { + if(process.Id == current.Id) continue; + if(process.MainModule.FileName == fileName) + { + ShowWindowAsync(process.MainWindowHandle, 1);//SW_SHOWNORMAL + SetForegroundWindow(process.MainWindowHandle); + return true; + } + } + } + return false; + } + } + + public static void Restart() + { + using(Process process = new Process()) + { + process.StartInfo.FileName = Application.ExecutablePath; + process.StartInfo.Arguments = "Restart"; + process.Start(); + } + Application.Exit(); + } + + [DllImport("User32.dll")] + private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); + + [DllImport("User32.dll")] + private static extern bool SetForegroundWindow(IntPtr hWnd); + } +} \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/StringExtension.cs b/ContextMenuManager/BluePointLilac.Methods/StringExtension.cs similarity index 90% rename from ContextMenuManager/BulePointLilac.Methods/StringExtension.cs rename to ContextMenuManager/BluePointLilac.Methods/StringExtension.cs index a2575ea..166eebe 100644 --- a/ContextMenuManager/BulePointLilac.Methods/StringExtension.cs +++ b/ContextMenuManager/BluePointLilac.Methods/StringExtension.cs @@ -1,4 +1,4 @@ -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { //为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的string.IsNullOrWhiteSpace)写了这个扩展方法 public static class StringExtension diff --git a/ContextMenuManager/BulePointLilac.Methods/TextBoxExtension.cs b/ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs similarity index 95% rename from ContextMenuManager/BulePointLilac.Methods/TextBoxExtension.cs rename to ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs index 00c52b1..40170b3 100644 --- a/ContextMenuManager/BulePointLilac.Methods/TextBoxExtension.cs +++ b/ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs @@ -1,7 +1,7 @@ using System.Drawing; using System.Windows.Forms; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { public static class TextBoxExtension { diff --git a/ContextMenuManager/BulePointLilac.Methods/WindowsOsVersion.cs b/ContextMenuManager/BluePointLilac.Methods/WindowsOsVersion.cs similarity index 98% rename from ContextMenuManager/BulePointLilac.Methods/WindowsOsVersion.cs rename to ContextMenuManager/BluePointLilac.Methods/WindowsOsVersion.cs index 19fac75..5795cb4 100644 --- a/ContextMenuManager/BulePointLilac.Methods/WindowsOsVersion.cs +++ b/ContextMenuManager/BluePointLilac.Methods/WindowsOsVersion.cs @@ -1,6 +1,6 @@ using System; -namespace BulePointLilac.Methods +namespace BluePointLilac.Methods { //判断Windows系统版本 public static class WindowsOsVersion diff --git a/ContextMenuManager/BluePointLilac.Methods/WshShortcut.cs b/ContextMenuManager/BluePointLilac.Methods/WshShortcut.cs new file mode 100644 index 0000000..4daa4ee --- /dev/null +++ b/ContextMenuManager/BluePointLilac.Methods/WshShortcut.cs @@ -0,0 +1,202 @@ +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace BluePointLilac.Methods +{ + //为兼容.Net Framework 3.5无法使用dynamic和Interop.IWshRuntimeLibrary.dll专门写出此类 + public sealed class WshShortcut : IDisposable + { + private static readonly Type ShellType = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")); + private static readonly object Shell = Activator.CreateInstance(ShellType); + private static readonly BindingFlags InvokeMethodFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod; + private static readonly BindingFlags GetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty; + private static readonly BindingFlags SetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty; + + private static object CreateShortcut(string lnkPath) + { + return ShellType.InvokeMember("CreateShortcut", InvokeMethodFlag, null, Shell, new[] { lnkPath }); + } + + public WshShortcut(string lnkPath) + { + //只调用CreateShortcut方法时,TargetPath值为null, + //其他属性值正常,只有调用一下Save方法才能获取到TargetPath值 + this.lnkPath = lnkPath; + shortcut = CreateShortcut(lnkPath); + shortcutType = shortcut.GetType(); + shellLink = (IShellLinkA)new ShellLink(); + if(File.Exists(lnkPath)) + { + Save(); + ((IPersistFile)shellLink).Load(lnkPath, 2);//STGM_READWRITE + } + } + + private readonly string lnkPath; + private readonly object shortcut; + private readonly Type shortcutType; + private readonly IShellLinkA shellLink; + + public string FullName + { + get => GetValue("FullName")?.ToString(); + } + public string TargetPath + { + get => GetValue("TargetPath")?.ToString(); + set => SetValue("TargetPath", value); + } + public string Arguments + { + get => GetValue("Arguments")?.ToString(); + set => SetValue("Arguments", value); + } + public string WorkingDirectory + { + get => GetValue("WorkingDirectory")?.ToString(); + set => SetValue("WorkingDirectory", value); + } + public string IconLocation + { + get => GetValue("IconLocation")?.ToString(); + set => SetValue("IconLocation", value); + } + public string Description + { + get => GetValue("Description")?.ToString(); + set => SetValue("Description", value); + } + public string Hotkey + { + get => GetValue("Hotkey")?.ToString(); + set => SetValue("Hotkey", value); + } + public int WindowStyle + { + get => Convert.ToInt32(GetValue("WindowStyle")); + set => SetValue("WindowStyle", value); + } + public bool RunAsAdministrator + { + get + { + ((IShellLinkDataList)shellLink).GetFlags(out ShellLinkDataFlags flags); + return (flags & ShellLinkDataFlags.RunasUser) == ShellLinkDataFlags.RunasUser; + } + set + { + ((IShellLinkDataList)shellLink).GetFlags(out ShellLinkDataFlags flags); + if(value) flags |= ShellLinkDataFlags.RunasUser; + else flags &= ~ShellLinkDataFlags.RunasUser; + ((IShellLinkDataList)shellLink).SetFlags(flags); + ((IPersistFile)shellLink).Save(lnkPath, true); + } + } + + private object GetValue(string name) + { + try { return shortcutType.InvokeMember(name, GetPropertyFlag, null, shortcut, null); } + catch { return null; } + } + + private void SetValue(string name, object value) + { + shortcutType.InvokeMember(name, SetPropertyFlag, null, shortcut, new[] { value }); + } + + public void Save() + { + //存储快捷方式为写入文件行为,如果没有权限会报错 + shortcutType.InvokeMember("Save", InvokeMethodFlag, null, shortcut, null); + } + + public void Dispose() + { + Marshal.ReleaseComObject(shortcut); + Marshal.ReleaseComObject(shellLink); + } + + ~WshShortcut() { Dispose(); } + + + [ComImport] + [Guid("00021401-0000-0000-C000-000000000046")] + internal class ShellLink { } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214EE-0000-0000-C000-000000000046")] + public interface IShellLinkA + { + void GetPath([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags); + void GetIDList(out IntPtr ppidl); + void SetIDList(IntPtr pidl); + void GetDescription([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszName, int cchMaxName); + void SetDescription([MarshalAs(UnmanagedType.LPStr)] string pszName); + void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszDir, int cchMaxPath); + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPStr)] string pszDir); + void GetArguments([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszArgs, int cchMaxPath); + void SetArguments([MarshalAs(UnmanagedType.LPStr)] string pszArgs); + void GetHotkey(out short pwHotkey); + void SetHotkey(short wHotkey); + void GetShowCmd(out int piShowCmd); + void SetShowCmd(int iShowCmd); + void GetIconLocation([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); + void SetIconLocation([MarshalAs(UnmanagedType.LPStr)] string pszIconPath, int iIcon); + void SetRelativePath([MarshalAs(UnmanagedType.LPStr)] string pszPathRel, int dwReserved); + void Resolve(IntPtr hwnd, int fFlags); + void SetPath([MarshalAs(UnmanagedType.LPStr)] string pszFile); + } + + [ComImport] + [Guid("45e2b4ae-b1c3-11d0-b92f-00a0c90312e1")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IShellLinkDataList + { + void AddDataBlock(IntPtr pDataBlock); + void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock); + void RemoveDataBlock(uint dwSig); + void GetFlags(out ShellLinkDataFlags pdwFlags); + void SetFlags(ShellLinkDataFlags dwFlags); + } + + [Flags] // SHELL_LINK_DATA_FLAGS + public enum ShellLinkDataFlags : uint + { + Default = 0x00000000, + HasIdList = 0x00000001, + HasLinkInfo = 0x00000002, + HasName = 0x00000004, + HasRelpath = 0x00000008, + HasWorkingdir = 0x00000010, + HasArgs = 0x00000020, + HasIconLocation = 0x00000040, + Unicode = 0x00000080, + ForceNoLinkInfo = 0x00000100, + HasExpSz = 0x00000200, + RunInSeparate = 0x00000400, + HasLogo3Id = 0x00000800, + HasDarwinId = 0x00001000, + RunasUser = 0x00002000, + HasExpIconSz = 0x00004000, + NoPidlAlias = 0x00008000, + ForceUncname = 0x00010000, + RunWithShimlayer = 0x00020000, + ForceNoLinktrack = 0x00040000, + EnableTargetMetadata = 0x00080000, + DisableLinkPathTracking = 0x00100000, + DisableKnownfolderRelativeTracking = 0x00200000, + NoKFAlias = 0x00400000, + AllowLinkToLink = 0x00800000, + UnaliasOnSave = 0x01000000, + PreferEnvironmentPath = 0x02000000, + KeepLocalIdListForUncTarget = 0x04000000, + Valid = 0x07fff7ff, + Reserved = 0x80000000 + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs b/ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs deleted file mode 100644 index e5ac640..0000000 --- a/ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace BulePointLilac.Methods -{ - public static class PropertiesDialog - { - public static bool Show(string filePath) - { - SHELLEXECUTEINFO info = new SHELLEXECUTEINFO - { - lpVerb = "Properties", - //lpParameters = "详细信息";//显示选项卡,此处有语言差异 - lpFile = filePath, - nShow = SW_SHOW, - fMask = SEE_MASK_INVOKEIDLIST, - cbSize = CbSize - }; - return ShellExecuteEx(ref info); - } - - private const int SW_SHOW = 5; - - private const uint SEE_MASK_INVOKEIDLIST = 12; - - private static readonly int CbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)); - - [DllImport("shell32.dll", CharSet = CharSet.Auto)] - private static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - private struct SHELLEXECUTEINFO - { - public int cbSize; - public uint fMask; - public IntPtr hwnd; - [MarshalAs(UnmanagedType.LPTStr)] - public string lpVerb; - [MarshalAs(UnmanagedType.LPTStr)] - public string lpFile; - [MarshalAs(UnmanagedType.LPTStr)] - public string lpParameters; - [MarshalAs(UnmanagedType.LPTStr)] - public string lpDirectory; - public int nShow; - public IntPtr hInstApp; - public IntPtr lpIDList; - [MarshalAs(UnmanagedType.LPTStr)] - public string lpClass; - public IntPtr hkeyClass; - public uint dwHotKey; - public IntPtr hIcon; - public IntPtr hProcess; - } - } -} \ No newline at end of file diff --git a/ContextMenuManager/BulePointLilac.Methods/WshShortcut.cs b/ContextMenuManager/BulePointLilac.Methods/WshShortcut.cs deleted file mode 100644 index 0dbd0e4..0000000 --- a/ContextMenuManager/BulePointLilac.Methods/WshShortcut.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Reflection; - -namespace BulePointLilac.Methods -{ - //为兼容.Net Framework 3.5无法使用dynamic和Interop.IWshRuntimeLibrary.dll专门写出此类 - sealed class WshShortcut - { - private static readonly Type ShellType = Type.GetTypeFromProgID("WScript.Shell"); - private static readonly object Shell = Activator.CreateInstance(ShellType); - private static readonly BindingFlags InvokeMethodFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod; - private static readonly BindingFlags GetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty; - private static readonly BindingFlags SetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty; - - private static object CreateShortcut(string lnkPath) - { - return ShellType.InvokeMember("CreateShortcut", InvokeMethodFlag, null, Shell, new[] { lnkPath }); - } - - private Type ShortcutType; - private object Shortcut; - - private string fullName; - public string FullName - { - get => fullName; - set => Load(value); - } - - public string TargetPath - { - get => GetValue("TargetPath").ToString(); - set => SetValue("TargetPath", value); - } - public string Arguments - { - get => GetValue("Arguments").ToString(); - set => SetValue("Arguments", value); - } - public string WorkingDirectory - { - get => GetValue("WorkingDirectory").ToString(); - set => SetValue("WorkingDirectory", value); - } - public string IconLocation - { - get => GetValue("IconLocation").ToString(); - set => SetValue("IconLocation", value); - } - public string Description - { - get => GetValue("Description").ToString(); - set => SetValue("Description", value); - } - public string Hotkey - { - get => GetValue("Hotkey").ToString(); - set => SetValue("Hotkey", value); - } - public int WindowStyle - { - get => Convert.ToInt32(GetValue("WindowStyle")); - set => SetValue("WindowStyle", value); - } - //public string RelativePath { get; set; }//暂时不知道这是啥 - - private object GetValue(string name) - { - return ShortcutType.InvokeMember(name, GetPropertyFlag, null, Shortcut, null); - } - - private void SetValue(string name, object value) - { - ShortcutType.InvokeMember(name, SetPropertyFlag, null, Shortcut, new[] { value }); - } - - public void Load(string lnkPath) - { - fullName = lnkPath; - Shortcut = CreateShortcut(lnkPath); - ShortcutType = Shortcut.GetType(); - Save(); - } - - public void Save() - { - //存储快捷方式为写入文件行为,如果没有权限会报错 - ShortcutType.InvokeMember("Save", InvokeMethodFlag, null, Shortcut, null); - } - } -} \ No newline at end of file diff --git a/ContextMenuManager/ContextMenuManager.csproj b/ContextMenuManager/ContextMenuManager.csproj index 33ca476..7c73ab0 100644 --- a/ContextMenuManager/ContextMenuManager.csproj +++ b/ContextMenuManager/ContextMenuManager.csproj @@ -110,46 +110,67 @@ - - + + Component - + Form - + + Component + + + + + + + + + + + + + + + + Component - - - - - - - - - - - - + Component - + Component - + Component - + Component Component + + Component + + + Component + Component - + + Component + + + Component + + + Component + + Component @@ -200,9 +221,6 @@ Component - - Component - Component @@ -212,16 +230,13 @@ Component - - Component - Component Component - + Component @@ -248,6 +263,9 @@ Component + + Component + Component @@ -257,31 +275,31 @@ Form - + Component - + Component - + Component - + Component - + Form - + Component - + Component Component - + Component @@ -290,20 +308,24 @@ Component - - - - - + + + + - + - - - - + + + + + + True + True + Resources.resx + @@ -314,11 +336,6 @@ Settings.Designer.cs - - True - True - Resources.resx - True Settings.settings @@ -339,6 +356,10 @@ + + + + @@ -346,20 +367,17 @@ - - - diff --git a/ContextMenuManager/Controls/AboutApp.cs b/ContextMenuManager/Controls/AboutApp.cs index ddf1c06..076f795 100644 --- a/ContextMenuManager/Controls/AboutApp.cs +++ b/ContextMenuManager/Controls/AboutApp.cs @@ -1,13 +1,10 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.IO; -using System.Runtime.InteropServices; using System.Text; -using System.Threading; using System.Windows.Forms; namespace ContextMenuManager.Controls @@ -22,7 +19,7 @@ public DonateBox() this.BackColor = Color.White; this.Font = new Font(SystemFonts.MenuFont.FontFamily, 10F); this.Controls.AddRange(new Control[] { lblInfo, picQR, llbDonationList }); - llbDonationList.LinkClicked += (sender, e) => Process.Start(DonateListUrl); + llbDonationList.LinkClicked += (sender, e) => ExternalProgram.OpenUrl(DonateListUrl); } readonly Label lblInfo = new Label @@ -70,12 +67,12 @@ public DictionariesBox() boxs[i] = new ReadOnlyRichTextBox { Parent = pages[i] }; if(i > 0) boxs[i].ContextMenuStrip = cms; } - items[0].Click += (sender, e) => EditText(); + items[0].Click += (sender, e) => ExternalProgram.OpenNotepadWithText(GetInitialText()); items[2].Click += (sender, e) => SaveFile(); boxs[0].Controls.Add(btnOpenDir); btnOpenDir.Top = boxs[0].Height - btnOpenDir.Height; MyToolTip.SetToolTip(btnOpenDir, AppString.Tip.OpenDictionariesDir); - btnOpenDir.MouseDown += (sender, e) => Process.Start(AppConfig.DicsDir); + btnOpenDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.DicsDir); } readonly TabPage[] pages = new TabPage[] { @@ -98,22 +95,6 @@ public DictionariesBox() new ToolStripMenuItem(AppString.Menu.Save) }; - [DllImport("user32.dll")] - private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); - - [DllImport("user32.dll")] - private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam); - const int WM_SETTEXT = 0x000C; - private void EditText() - { - using(Process process = Process.Start("notepad.exe")) - { - Thread.Sleep(200); - IntPtr handle = FindWindowEx(process.MainWindowHandle, IntPtr.Zero, "Edit", null); - SendMessage(handle, WM_SETTEXT, 0, GetInitialText()); - } - } - private void SaveFile() { using(SaveFileDialog dlg = new SaveFileDialog()) @@ -140,7 +121,7 @@ private void SaveFile() dlg.InitialDirectory = dirPath; if(dlg.ShowDialog() == DialogResult.OK) { - File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.UTF8); + File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.Unicode); } } } @@ -186,8 +167,8 @@ public LanguagesBox() this.Controls.AddRange(new Control[] { cmbLanguages, btnOpenDir, llbOtherLanguages, txtTranslators }); this.OnResize(null); cmbLanguages.SelectionChangeCommitted += (sender, e) => ChangeLanguage(); - llbOtherLanguages.LinkClicked += (sender, e) => Process.Start(OtherLanguagesUrl); - btnOpenDir.MouseDown += (sender, e) => Process.Start(AppConfig.LangsDir); + llbOtherLanguages.LinkClicked += (sender, e) => ExternalProgram.OpenUrl(OtherLanguagesUrl); + btnOpenDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.LangsDir); MyToolTip.SetToolTip(btnOpenDir, AppString.Tip.OpenLanguagesDir); } @@ -237,7 +218,7 @@ public void LoadLanguages() if(Directory.Exists(AppConfig.LangsDir)) { languages.Clear(); - foreach(string fileName in Directory.GetFiles(AppConfig.LangsDir,"*.ini")) + foreach(string fileName in Directory.GetFiles(AppConfig.LangsDir, "*.ini")) { languages.Add(Path.GetFileNameWithoutExtension(fileName)); IniReader reader = new IniReader(fileName); @@ -264,7 +245,7 @@ private void ChangeLanguage() else { AppConfig.Language = language; - Application.Restart(); + SingleInstance.Restart(); } } @@ -281,8 +262,8 @@ private int GetSelectIndex() sealed class AppSettingBox : MyList { - private const string GITHUBRELEASES = "https://github.com/BluePointLilac/ContextMenuManager/releases"; - private const string GITEERELEASES = "https://gitee.com/BluePointLilac/ContextMenuManager/releases"; + private const string GithubUrl = "https://github.com/BluePointLilac/ContextMenuManager/releases"; + private const string GiteeUrl = "https://gitee.com/BluePointLilac/ContextMenuManager/releases"; public AppSettingBox() { @@ -292,16 +273,20 @@ public AppSettingBox() mliUpdate.AddCtrs(new Control[] { lblGitee, lblGithub, lblUpdate }); mliProtect.AddCtr(chkProtect); mliEngine.AddCtr(cmbEngine); + mliWinXSortable.AddCtr(chkWinXSortable); + mliShowFilePath.AddCtr(chkShowFilePath); + mliOpenMoreRegedit.AddCtr(chkOpenMoreRegedit); + MyToolTip.SetToolTip(cmbConfigDir, AppString.Tip.ConfigPath); MyToolTip.SetToolTip(btnConfigDir, AppString.Other.OpenConfigDir); MyToolTip.SetToolTip(btnBackupDir, AppString.Other.OpenBackupDir); MyToolTip.SetToolTip(lblUpdate, AppString.Tip.CheckUpdate + Environment.NewLine + AppString.Tip.LastCheckUpdateTime + AppConfig.LastCheckUpdateTime.ToLongDateString()); - cmbConfigDir.Items.AddRange(new[] { AppString.Other.SaveToAppData, AppString.Other.SaveToAppDir }); + cmbConfigDir.Items.AddRange(new[] { AppString.Other.AppDataDir, AppString.Other.AppDir }); cmbEngine.Items.AddRange(new[] { "Baidu", "Bing", "Google", "DogeDoge", "Sogou", "360", AppString.Other.CustomEngine }); - btnConfigDir.MouseDown += (sender, e) => Process.Start(AppConfig.ConfigDir); - btnBackupDir.MouseDown += (sender, e) => Process.Start(AppConfig.BackupDir); - lblGithub.Click += (sender, e) => Process.Start(GITHUBRELEASES); - lblGitee.Click += (sender, e) => Process.Start(GITEERELEASES); + btnConfigDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.ConfigDir); + btnBackupDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.ConfigDir); + lblGithub.Click += (sender, e) => ExternalProgram.OpenUrl(GithubUrl); + lblGitee.Click += (sender, e) => ExternalProgram.OpenUrl(GiteeUrl); lblUpdate.Click += (sender, e) => { if(!Updater.CheckUpdate()) MessageBoxEx.Show(AppString.MessageBox.NoUpdateDetected); @@ -318,7 +303,7 @@ public AppSettingBox() { DirectoryEx.CopyTo(AppConfig.ConfigDir, newPath); Directory.Delete(AppConfig.ConfigDir, true); - Application.Restart(); + SingleInstance.Restart(); } }; cmbEngine.SelectionChangeCommitted += (sender, e) => @@ -346,17 +331,32 @@ public AppSettingBox() }; chkBackup.MouseDown += (sender, e) => AppConfig.AutoBackup = chkBackup.Checked = !chkBackup.Checked; chkProtect.MouseDown += (sender, e) => AppConfig.ProtectOpenItem = chkProtect.Checked = !chkProtect.Checked; + chkWinXSortable.MouseDown += (sender, e) => AppConfig.WinXSortable = chkWinXSortable.Checked = !chkWinXSortable.Checked; + chkOpenMoreRegedit.MouseDown += (sender, e) => AppConfig.OpenMoreRegedit = chkOpenMoreRegedit.Checked = !chkOpenMoreRegedit.Checked; + chkShowFilePath.MouseDown += (sender, e) => + { + chkShowFilePath.Checked = !chkShowFilePath.Checked; + if(MessageBoxEx.Show(AppString.MessageBox.RestartApp, MessageBoxButtons.OKCancel) == DialogResult.OK) + { + AppConfig.ShowFilePath = chkShowFilePath.Checked; + SingleInstance.Restart(); + } + else + { + chkShowFilePath.Checked = !chkShowFilePath.Checked; + } + }; } readonly MyListItem mliConfigDir = new MyListItem { - Text = AppString.Other.ConfigFile, + Text = AppString.Other.ConfigPath, HasImage = false }; readonly ComboBox cmbConfigDir = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, - Width = 160.DpiZoom() + Width = 120.DpiZoom() }; readonly PictureButton btnConfigDir = new PictureButton(AppImage.Open); @@ -410,15 +410,42 @@ public AppSettingBox() readonly ComboBox cmbEngine = new ComboBox { DropDownStyle = ComboBoxStyle.DropDownList, - Width = 100.DpiZoom() + Width = 120.DpiZoom() }; + readonly MyListItem mliWinXSortable = new MyListItem + { + Text = AppString.Item.WinXSortable, + Visible = WindowsOsVersion.ISAfterOrEqual8, + HasImage = false + }; + readonly MyCheckBox chkWinXSortable = new MyCheckBox(); + + readonly MyListItem mliShowFilePath = new MyListItem + { + Text = AppString.Item.ShowFilePath, + HasImage = false + }; + readonly MyCheckBox chkShowFilePath = new MyCheckBox(); + + readonly MyListItem mliOpenMoreRegedit = new MyListItem + { + Text = AppString.Item.OpenMoreRegedit, + HasImage = false + }; + readonly MyCheckBox chkOpenMoreRegedit = new MyCheckBox(); + public void LoadItems() { - this.AddItems(new[] { mliUpdate, mliConfigDir, mliBackup, mliProtect, mliEngine }); + this.AddItems(new[] { mliUpdate, mliConfigDir, mliBackup, mliProtect, + mliEngine, mliWinXSortable, mliShowFilePath, mliOpenMoreRegedit }); cmbConfigDir.SelectedIndex = AppConfig.SaveToAppDir ? 1 : 0; chkBackup.Checked = AppConfig.AutoBackup; chkProtect.Checked = AppConfig.ProtectOpenItem; + chkWinXSortable.Checked = AppConfig.WinXSortable; + chkShowFilePath.Checked = AppConfig.ShowFilePath; + chkOpenMoreRegedit.Checked = AppConfig.OpenMoreRegedit; + string url = AppConfig.EngineUrl; for(int i = 0; i <= AppConfig.EngineUrls.Length; i++) { diff --git a/ContextMenuManager/Controls/AddGuidDicDialog.cs b/ContextMenuManager/Controls/AddGuidDicDialog.cs index dbe91e0..001f6eb 100644 --- a/ContextMenuManager/Controls/AddGuidDicDialog.cs +++ b/ContextMenuManager/Controls/AddGuidDicDialog.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; diff --git a/ContextMenuManager/Controls/CommandDialog.cs b/ContextMenuManager/Controls/CommandDialog.cs deleted file mode 100644 index 86751db..0000000 --- a/ContextMenuManager/Controls/CommandDialog.cs +++ /dev/null @@ -1,107 +0,0 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; -using System; -using System.Drawing; -using System.Windows.Forms; - -namespace ContextMenuManager.Controls -{ - sealed class CommandDialog : CommonDialog - { - public string Command { get; set; } - public string Arguments { get; set; } - - public override void Reset() { } - - protected override bool RunDialog(IntPtr hwndOwner) - { - using(CommandForm frm = new CommandForm()) - { - frm.Command = this.Command; - frm.Arguments = this.Arguments; - bool flag = frm.ShowDialog() == DialogResult.OK; - if(flag) - { - this.Command = frm.Command; - this.Arguments = frm.Arguments; - } - return flag; - } - } - - sealed class CommandForm : ResizbleForm - { - public CommandForm() - { - this.AcceptButton = btnOk; - this.CancelButton = btnCancel; - this.VerticalResizable = false; - this.Font = SystemFonts.MessageBoxFont; - this.Text = AppString.Menu.ChangeCommand; - this.SizeGripStyle = SizeGripStyle.Hide; - this.StartPosition = FormStartPosition.CenterParent; - this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false; - InitializeComponents(); - } - - public string Command - { - get => txtCommand.Text; - set => txtCommand.Text = value; - } - - public string Arguments - { - get => txtArguments.Text; - set => txtArguments.Text = value; - } - - readonly Label lblCommand = new Label - { - Text = AppString.Dialog.ItemCommand, - AutoSize = true - }; - readonly Label lblArguments = new Label - { - Text = AppString.Dialog.CommandArguments, - AutoSize = true - }; - readonly TextBox txtCommand = new TextBox(); - readonly TextBox txtArguments = new TextBox(); - readonly Button btnOk = new Button - { - DialogResult = DialogResult.OK, - Text = AppString.Dialog.Ok, - AutoSize = true - }; - readonly Button btnCancel = new Button - { - DialogResult = DialogResult.Cancel, - Text = AppString.Dialog.Cancel, - AutoSize = true - }; - - private void InitializeComponents() - { - this.Controls.AddRange(new Control[] { lblCommand, lblArguments, txtCommand, txtArguments, btnOk, btnCancel }); - int a = 20.DpiZoom(); - lblArguments.Left = lblCommand.Left = lblCommand.Top = txtCommand.Top = a; - lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a; - btnOk.Top = btnCancel.Top = txtArguments.Bottom + a; - int b = Math.Max(lblCommand.Width, lblArguments.Width) + 3 * a; - this.ClientSize = new Size(250.DpiZoom() + b, btnOk.Bottom + a); - btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top; - btnCancel.Left = this.ClientSize.Width - btnCancel.Width - a; - btnOk.Left = btnCancel.Left - btnOk.Width - a; - this.Resize += (sender, e) => - { - txtArguments.Width = txtCommand.Width = this.ClientSize.Width - b; - txtArguments.Left = txtCommand.Left = btnCancel.Right - txtCommand.Width; - }; - this.OnResize(null); - this.MinimumSize = this.Size; - } - } - } - -} diff --git a/ContextMenuManager/Controls/EnhanceMenusItem.cs b/ContextMenuManager/Controls/EnhanceMenusItem.cs index e902ecb..a69674f 100644 --- a/ContextMenuManager/Controls/EnhanceMenusItem.cs +++ b/ContextMenuManager/Controls/EnhanceMenusItem.cs @@ -1,10 +1,12 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; using System.Xml; namespace ContextMenuManager.Controls @@ -63,8 +65,16 @@ private static void WriteAttributesValue(XmlNode valueXN, string regPath) private static void WriteSubKeysValue(XmlElement keyXE, string regPath) { if(keyXE == null) return; - string defaultValue = keyXE.GetAttribute("Default"); - if(!defaultValue.IsNullOrWhiteSpace()) Registry.SetValue(regPath, "", defaultValue); + string defaultValue = Environment.ExpandEnvironmentVariables(keyXE.GetAttribute("Default")); + if(!defaultValue.IsNullOrWhiteSpace()) + { + Registry.SetValue(regPath, "", defaultValue); + } + else if(keyXE.Name == "Command") + { + //按照规则Command节点无默认值则创建文件 + WriteCommandValue(keyXE, regPath); + } WriteAttributesValue(keyXE.SelectSingleNode("Value"), regPath); XmlNode subKeyXN = keyXE.SelectSingleNode("SubKey"); @@ -74,6 +84,50 @@ private static void WriteSubKeysValue(XmlElement keyXE, string regPath) WriteSubKeysValue(xe, $@"{regPath}\{xe.Name}"); } } + + private static void WriteCommandValue(XmlElement cmdXE, string regPath) + { + XmlElement fnXE = (XmlElement)cmdXE.SelectSingleNode("FileName"); + XmlElement argXE = (XmlElement)cmdXE.SelectSingleNode("Arguments"); + XmlElement seXE = (XmlElement)cmdXE.SelectSingleNode("ShellExecute"); + + string command; + string fileName = fnXE?.InnerText.Trim(); + string arguments = argXE?.InnerText.Trim(); + if(string.IsNullOrEmpty(fileName)) fileName = CreateCommandFile(fnXE); + if(string.IsNullOrEmpty(arguments)) arguments = CreateCommandFile(argXE); + fileName = Environment.ExpandEnvironmentVariables(fileName); + arguments = Environment.ExpandEnvironmentVariables(arguments); + if(seXE != null) + { + string verb = seXE.HasAttribute("Verb") ? seXE.GetAttribute("Verb") : "open"; + int windowStyle = seXE.HasAttribute("WindowStyle") ? Convert.ToInt32(seXE.GetAttribute("WindowStyle")) : 1; + string directory = Environment.ExpandEnvironmentVariables(seXE.GetAttribute("Directory")); + command = ShellExecuteDialog.GetCommand(fileName, arguments, verb, windowStyle, directory); + } + else + { + command = fileName; + if(arguments != string.Empty) command += $" {arguments}"; + } + Registry.SetValue(regPath, "", command); + } + + private static string CreateCommandFile(XmlElement xe) + { + if(xe == null) return string.Empty; + XmlElement cfXE = (XmlElement)xe.SelectSingleNode("CreateFile"); + if(cfXE == null) return string.Empty; + string fileName = cfXE.GetAttribute("FileName"); + string content = cfXE.GetAttribute("Content"); + string extension = Path.GetExtension(fileName).ToLower(); + string filePath = $@"{AppConfig.ProgramsDir}\{fileName}"; + Encoding encoding = Encoding.Unicode; + if(extension == ".bat" || extension == ".cmd") encoding = Encoding.Default; + if(File.Exists(filePath)) File.Delete(filePath); + File.WriteAllText(filePath, content, encoding); + return filePath; + } } sealed class EnhanceShellExItem : MyListItem, IFoldSubItem, IChkVisibleItem diff --git a/ContextMenuManager/Controls/EnhanceMenusList.cs b/ContextMenuManager/Controls/EnhanceMenusList.cs index e57efc3..6b68d2d 100644 --- a/ContextMenuManager/Controls/EnhanceMenusList.cs +++ b/ContextMenuManager/Controls/EnhanceMenusList.cs @@ -1,10 +1,9 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using System; using System.Drawing; using System.IO; -using System.Text; using System.Xml; namespace ContextMenuManager.Controls @@ -17,71 +16,9 @@ public void LoadItems() { foreach(XmlNode xn in ReadXml().DocumentElement.ChildNodes) { - string path = null; - string text = null; - Image image = null; - switch(xn.Name) - { - case "File": - path = ShellList.MENUPATH_FILE; - text = AppString.SideBar.File; - image = AppImage.File; - break; - case "Folder": - path = ShellList.MENUPATH_FOLDER; - text = AppString.SideBar.Folder; - image = AppImage.Folder; - break; - case "Directory": - path = ShellList.MENUPATH_FOLDER; - text = AppString.SideBar.Directory; - image = AppImage.Directory; - break; - case "Background": - path = ShellList.MENUPATH_BACKGROUND; - text = AppString.SideBar.Background; - image = AppImage.Background; - break; - case "Desktop": - path = ShellList.MENUPATH_DESKTOP; - //Vista没有桌面右键菜单的独立注册表项 - if(WindowsOsVersion.IsEqualVista) path = ShellList.MENUPATH_BACKGROUND; - text = AppString.SideBar.Desktop; - image = AppImage.Desktop; - break; - case "Drive": - path = ShellList.MENUPATH_DRIVE; - text = AppString.SideBar.Drive; - image = AppImage.Drive; - break; - case "AllObjects": - path = ShellList.MENUPATH_ALLOBJECTS; - text = AppString.SideBar.AllObjects; - image = AppImage.AllObjects; - break; - case "Computer": - path = ShellList.MENUPATH_COMPUTER; - text = AppString.SideBar.Computer; - image = AppImage.Computer; - break; - case "RecycleBin": - path = ShellList.MENUPATH_RECYCLEBIN; - text = AppString.SideBar.RecycleBin; - image = AppImage.RecycleBin; - break; - default: - XmlElement xe = (XmlElement)xn; - path = xe.GetAttribute("RegPath"); - text = ResourceString.GetDirectString(xe.GetAttribute("Text")); - image = ResourceIcon.GetIcon(xe.GetAttribute("Icon"))?.ToBitmap() ?? AppImage.NotFound; - break; - } - if(string.IsNullOrEmpty(path) || string.IsNullOrEmpty(text)) continue; - GroupPathItem groupItem = new GroupPathItem(path, ObjectPath.PathType.Registry) - { - Image = image, - Text = text, - }; + + GroupPathItem groupItem = GetGroupPathItem(xn); + if(groupItem == null) continue; this.AddItem(groupItem); XmlElement shellXE = (XmlElement)xn.SelectSingleNode("Shell"); XmlElement shellExXE = (XmlElement)xn.SelectSingleNode("ShellEx"); @@ -94,21 +31,89 @@ public void LoadItems() catch { } } + private GroupPathItem GetGroupPathItem(XmlNode xn) + { + string path; + string text; + Image image; + switch(xn.Name) + { + case "File": + path = ShellList.MENUPATH_FILE; + text = AppString.SideBar.File; + image = AppImage.File; + break; + case "Folder": + path = ShellList.MENUPATH_FOLDER; + text = AppString.SideBar.Folder; + image = AppImage.Folder; + break; + case "Directory": + path = ShellList.MENUPATH_FOLDER; + text = AppString.SideBar.Directory; + image = AppImage.Directory; + break; + case "Background": + path = ShellList.MENUPATH_BACKGROUND; + text = AppString.SideBar.Background; + image = AppImage.Background; + break; + case "Desktop": + path = ShellList.MENUPATH_DESKTOP; + //Vista没有桌面右键菜单的独立注册表项 + if(WindowsOsVersion.IsEqualVista) path = ShellList.MENUPATH_BACKGROUND; + text = AppString.SideBar.Desktop; + image = AppImage.Desktop; + break; + case "Drive": + path = ShellList.MENUPATH_DRIVE; + text = AppString.SideBar.Drive; + image = AppImage.Drive; + break; + case "AllObjects": + path = ShellList.MENUPATH_ALLOBJECTS; + text = AppString.SideBar.AllObjects; + image = AppImage.AllObjects; + break; + case "Computer": + path = ShellList.MENUPATH_COMPUTER; + text = AppString.SideBar.Computer; + image = AppImage.Computer; + break; + case "RecycleBin": + path = ShellList.MENUPATH_RECYCLEBIN; + text = AppString.SideBar.RecycleBin; + image = AppImage.RecycleBin; + break; + default: + XmlElement xe = (XmlElement)xn; + path = xe.GetAttribute("RegPath"); + text = ResourceString.GetDirectString(xe.GetAttribute("Text")); + if(string.IsNullOrEmpty(path) || string.IsNullOrEmpty(text)) return null; + image = ResourceIcon.GetIcon(xe.GetAttribute("Icon"))?.ToBitmap() ?? AppImage.NotFound; + break; + } + GroupPathItem groupItem = new GroupPathItem(path, ObjectPath.PathType.Registry) { Image = image, Text = text }; + return groupItem; + } + private XmlDocument ReadXml() { XmlDocument doc1 = new XmlDocument(); try { - //如果没有网络下载的,则将程序内置的写入 - if(!File.Exists(AppConfig.WebEnhanceMenusDic)) + if(File.Exists(AppConfig.WebEnhanceMenusDic)) + { + doc1.LoadXml(File.ReadAllText(AppConfig.WebEnhanceMenusDic, EncodingType.GetType(AppConfig.WebEnhanceMenusDic))); + } + else { - File.WriteAllText(AppConfig.WebEnhanceMenusDic, Properties.Resources.EnhanceMenusDic, Encoding.UTF8); + doc1.LoadXml(Properties.Resources.EnhanceMenusDic); } - doc1.Load(AppConfig.WebEnhanceMenusDic); if(File.Exists(AppConfig.UserEnhanceMenusDic)) { XmlDocument doc2 = new XmlDocument(); - doc2.Load(AppConfig.UserEnhanceMenusDic); + doc2.LoadXml(File.ReadAllText(AppConfig.UserEnhanceMenusDic, EncodingType.GetType(AppConfig.UserEnhanceMenusDic))); foreach(XmlNode xn in doc2.DocumentElement.ChildNodes) { XmlNode node = doc1.ImportNode(xn, true); @@ -122,13 +127,13 @@ private XmlDocument ReadXml() private void LoadShellItems(XmlElement shellXE, GroupPathItem groupItem) { - foreach(XmlElement itemXE in shellXE.GetElementsByTagName("Item")) + foreach(XmlElement itemXE in shellXE.SelectNodes("Item")) { if(!JudgeOSVersion(itemXE)) continue; + if(!FileExists(itemXE)) continue; XmlElement szXE = (XmlElement)itemXE.SelectSingleNode("Value/REG_SZ"); string keyName = itemXE.GetAttribute("KeyName"); if(keyName.IsNullOrWhiteSpace()) continue; - string regPath = $@"{groupItem.TargetPath}\shell\{keyName}"; EnhanceShellItem item = new EnhanceShellItem() { RegPath = $@"{groupItem.TargetPath}\shell\{keyName}", @@ -140,18 +145,40 @@ private void LoadShellItems(XmlElement shellXE, GroupPathItem groupItem) item.Text = ResourceString.GetDirectString(szXE.GetAttribute("MUIVerb")); if(szXE.HasAttribute("Icon")) item.Image = ResourceIcon.GetIcon(szXE.GetAttribute("Icon"))?.ToBitmap(); else if(szXE.HasAttribute("HasLUAShield")) item.Image = AppImage.Shield; + else + { + XmlElement cmdXE = (XmlElement)itemXE.SelectSingleNode("SubKey/Command"); + if(cmdXE != null) + { + Icon icon = null; + if(cmdXE.HasAttribute("Default")) + { + string filePath = ObjectPath.ExtractFilePath(cmdXE.GetAttribute("Default")); + icon = ResourceIcon.GetIcon(filePath); + } + item.Image = icon?.ToBitmap(); + icon?.Dispose(); + } + } } if(item.Image == null) item.Image = AppImage.NotFound; if(item.Text.IsNullOrWhiteSpace()) item.Text = keyName; item.ChkVisible.Checked = item.ItemVisible; - MyToolTip.SetToolTip(item.ChkVisible, itemXE.GetAttribute("Tip")); + string tip = itemXE.GetAttribute("Tip"); + if(itemXE.GetElementsByTagName("CreateFile").Count > 0) + { + if(!tip.IsNullOrWhiteSpace()) tip += "\n"; + tip += AppString.Tip.CommandFiles; + if(System.Diagnostics.Debugger.IsAttached) item.ChkVisible.Checked = item.ItemVisible = true; + } + MyToolTip.SetToolTip(item.ChkVisible, tip); this.AddItem(item); } } private void LoadShellExItems(XmlElement shellExXE, GroupPathItem groupItem) { - foreach(XmlElement itemXE in shellExXE.GetElementsByTagName("Item")) + foreach(XmlElement itemXE in shellExXE.SelectNodes("Item")) { if(!JudgeOSVersion(itemXE)) continue; if(!GuidEx.TryParse(itemXE.GetAttribute("Guid"), out Guid guid)) continue; @@ -174,6 +201,7 @@ private void LoadShellExItems(XmlElement shellExXE, GroupPathItem groupItem) public static bool JudgeOSVersion(XmlElement itemXE) { + if(System.Diagnostics.Debugger.IsAttached) return true;//调试状态 bool JudgeOne(XmlElement osXE) { Version ver = new Version(osXE.InnerText); @@ -197,11 +225,22 @@ bool JudgeOne(XmlElement osXE) } } - foreach(XmlElement osXE in itemXE.GetElementsByTagName("OSVersion")) + foreach(XmlElement osXE in itemXE.SelectNodes("OSVersion")) { if(!JudgeOne(osXE)) return false; } return true; } + + private static bool FileExists(XmlElement itemXE) + { + if(System.Diagnostics.Debugger.IsAttached) return true;//调试状态 + foreach(XmlElement feXE in itemXE.SelectNodes("FileExists")) + { + string path = Environment.ExpandEnvironmentVariables(feXE.InnerText); + if(!File.Exists(path)) return false; + } + return true; + } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/ExplorerRestarter.cs b/ContextMenuManager/Controls/ExplorerRestarter.cs index d94e048..0efaf4a 100644 --- a/ContextMenuManager/Controls/ExplorerRestarter.cs +++ b/ContextMenuManager/Controls/ExplorerRestarter.cs @@ -1,7 +1,6 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; -using System.Diagnostics; using System.Windows.Forms; namespace ContextMenuManager.Controls @@ -17,8 +16,8 @@ public ExplorerRestarter() MyToolTip.SetToolTip(BtnRestart, AppString.Tip.RestartExplorer); this.AddCtr(BtnRestart); this.CanMoveForm(); - BtnRestart.MouseDown += (sender, e) => { Explorer.ReStart(); this.Visible = false; }; - RestartHandler += (sender, e) => this.Visible = NeedRestart; + BtnRestart.MouseDown += (sender, e) => { ExternalProgram.RestartExplorer(); this.Visible = false; }; + ShowHandler += (sender, e) => this.Visible = true; } protected override void OnVisibleChanged(EventArgs e) @@ -29,38 +28,8 @@ protected override void OnVisibleChanged(EventArgs e) private readonly PictureButton BtnRestart = new PictureButton(AppImage.RestartExplorer); - private static event EventHandler RestartHandler; + private static event EventHandler ShowHandler; - private static bool needRestart; - public static bool NeedRestart - { - get => needRestart; - set - { - needRestart = value; - RestartHandler?.Invoke(null, null); - } - } - } - - public static class Explorer - { - public static void ReStart() - { - using(Process process = new Process()) - { - process.StartInfo = new ProcessStartInfo - { - FileName = "cmd.exe", - Arguments = "/c taskkill -f -im explorer.exe", - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true - }; - process.Start(); - process.WaitForExit(); - process.StartInfo.Arguments = "/c explorer"; - process.Start(); - } - } + public new static void Show() { ShowHandler?.Invoke(null, null); } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/FileExtensionDialog.cs b/ContextMenuManager/Controls/FileExtensionDialog.cs deleted file mode 100644 index 4396129..0000000 --- a/ContextMenuManager/Controls/FileExtensionDialog.cs +++ /dev/null @@ -1,89 +0,0 @@ -using BulePointLilac.Methods; -using System; -using System.Drawing; -using System.Windows.Forms; - -namespace ContextMenuManager.Controls -{ - sealed class FileExtensionDialog : CommonDialog - { - public string Extension { get; private set; } - public override void Reset() { } - - protected override bool RunDialog(IntPtr hwndOwner) - { - using(FileExtensionForm frm = new FileExtensionForm()) - { - bool flag = frm.ShowDialog() == DialogResult.OK; - if(flag) this.Extension = frm.Extension; - return flag; - } - } - - sealed class FileExtensionForm : Form - { - public FileExtensionForm() - { - this.AcceptButton = btnOk; - this.CancelButton = btnCancel; - this.Text = AppString.Dialog.SelectExtension; - this.Font = SystemFonts.MenuFont; - this.ShowIcon = this.ShowInTaskbar = false; - this.MaximizeBox = this.MinimizeBox = false; - this.FormBorderStyle = FormBorderStyle.FixedSingle; - this.StartPosition = FormStartPosition.CenterParent; - InitializeComponents(); - LoadExtensions(); - btnOk.Click += (sender, e) => - { - int index = cmbExtension.Text.IndexOf('.'); - if(index >= 0) this.Extension = cmbExtension.Text.Substring(index); - else this.Extension = $".{cmbExtension.Text}"; - }; - } - - public string Extension { get; private set; } - - readonly ComboBox cmbExtension = new ComboBox - { - AutoCompleteMode = AutoCompleteMode.SuggestAppend, - AutoCompleteSource = AutoCompleteSource.ListItems, - DropDownHeight = 294.DpiZoom(), - ImeMode = ImeMode.Disable - }; - readonly Button btnOk = new Button - { - DialogResult = DialogResult.OK, - Text = AppString.Dialog.Ok, - AutoSize = true - }; - readonly Button btnCancel = new Button - { - DialogResult = DialogResult.Cancel, - Text = AppString.Dialog.Cancel, - AutoSize = true - }; - - private void InitializeComponents() - { - this.Controls.AddRange(new Control[] { cmbExtension, btnOk, btnCancel }); - int a = 20.DpiZoom(); - cmbExtension.Left = a; - cmbExtension.Width = 85.DpiZoom(); - cmbExtension.Top = btnOk.Top = btnCancel.Top = a; - btnOk.Left = cmbExtension.Right + a; - btnCancel.Left = btnOk.Right + a; - this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a); - } - - private void LoadExtensions() - { - foreach(string extension in Microsoft.Win32.Registry.ClassesRoot.GetSubKeyNames()) - { - if(!extension.StartsWith(".")) continue; - cmbExtension.Items.Add(extension.Substring(1)); - } - } - } - } -} \ No newline at end of file diff --git a/ContextMenuManager/Controls/GuidBlockedItem.cs b/ContextMenuManager/Controls/GuidBlockedItem.cs index 2c94a6f..66e06e5 100644 --- a/ContextMenuManager/Controls/GuidBlockedItem.cs +++ b/ContextMenuManager/Controls/GuidBlockedItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using System; using System.Windows.Forms; @@ -63,7 +63,7 @@ private void InitializeComponents() public void DeleteMe() { Array.ForEach(BlockedPaths, path => { RegistryEx.DeleteValue(path, this.Value); }); - if(!this.Guid.Equals(Guid.Empty)) ExplorerRestarter.NeedRestart = true; + if(!this.Guid.Equals(Guid.Empty)) ExplorerRestarter.Show(); this.Dispose(); } } diff --git a/ContextMenuManager/Controls/GuidBlockedList.cs b/ContextMenuManager/Controls/GuidBlockedList.cs index 006d881..e846c7a 100644 --- a/ContextMenuManager/Controls/GuidBlockedList.cs +++ b/ContextMenuManager/Controls/GuidBlockedList.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using Microsoft.Win32; using System; using System.Collections.Generic; @@ -32,17 +32,9 @@ private void LoadBlockedItems() private void AddNewItem() { - MyListItem newItem = new MyListItem - { - Text = AppString.Item.AddGuidBlockedItem, - Image = AppImage.AddNewItem - }; - PictureButton btnAddNewItem = new PictureButton(AppImage.AddNewItem); - newItem.AddCtr(btnAddNewItem); - newItem.SetNoClickEvent(); + NewItem newItem = new NewItem(AppString.Item.AddGuidBlockedItem); this.AddItem(newItem); - MyToolTip.SetToolTip(btnAddNewItem, newItem.Text); - btnAddNewItem.MouseDown += (sender, e) => + newItem.AddNewItem += (sender, e) => { using(InputDialog dlg = new InputDialog { Title = AppString.Dialog.InputGuid }) { @@ -63,7 +55,7 @@ private void AddNewItem() } } this.InsertItem(new GuidBlockedItem(dlg.Text), 1); - ExplorerRestarter.NeedRestart = true; + ExplorerRestarter.Show(); } else MessageBoxEx.Show(AppString.MessageBox.MalformedGuid); } diff --git a/ContextMenuManager/Controls/IEItem.cs b/ContextMenuManager/Controls/IEItem.cs new file mode 100644 index 0000000..8baf9f5 --- /dev/null +++ b/ContextMenuManager/Controls/IEItem.cs @@ -0,0 +1,122 @@ +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using ContextMenuManager.Controls.Interfaces; +using Microsoft.Win32; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + sealed class IEItem : MyListItem, ITsiRegPathItem, ITsiFilePathItem, ITsiRegDeleteItem, ITsiCommandItem, + ITsiWebSearchItem, ITsiTextItem, ITsiRegExportItem, IBtnShowMenuItem, IChkVisibleItem + { + public static readonly string[] MeParts = { "MenuExt", "-MenuExt" }; + + public IEItem(string regPath) + { + InitializeComponents(); + this.RegPath = regPath; + } + + private string regPath; + public string RegPath + { + get => regPath; + set + { + regPath = value; + this.Text = this.ItemText; + this.Image = this.ItemImage; + ChkVisible.Checked = this.ItemVisible; + } + } + public string ValueName => null; + private string KeyName => RegistryEx.GetKeyName(RegPath); + private string BackupPath => $@"{IEList.IEPath}\{(ItemVisible ? MeParts[1] : MeParts[0])}\{KeyName}"; + private string MeKeyName => RegistryEx.GetKeyName(RegistryEx.GetParentPath(RegPath)); + + public string ItemText + { + get => RegistryEx.GetKeyName(RegPath); + set + { + string newPath = $@"{RegistryEx.GetParentPath(RegPath)}\{value.Replace("\\", "")}"; + string defaultValue = Registry.GetValue(newPath, "", null)?.ToString(); + if(!defaultValue.IsNullOrWhiteSpace()) + { + MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded); + } + else + { + RegistryEx.MoveTo(RegPath, newPath); + RegPath = newPath; + } + } + } + + public bool ItemVisible + { + get => MeKeyName.Equals(MeParts[0], StringComparison.OrdinalIgnoreCase); + set + { + RegistryEx.MoveTo(RegPath, BackupPath); + RegPath = BackupPath; + } + } + + public string ItemCommand + { + get => Registry.GetValue(RegPath, "", null)?.ToString(); + set + { + Registry.SetValue(RegPath, "", value); + this.Image = this.ItemImage; + } + } + + public string SearchText => $@"{AppString.SideBar.IEMenu} {Text}"; + public string ItemFilePath => ObjectPath.ExtractFilePath(ItemCommand); + private Icon ItemIcon => ResourceIcon.GetIcon(ItemFilePath) ?? ResourceIcon.GetExtensionIcon(ItemFilePath); + private Image ItemImage => ItemIcon?.ToBitmap() ?? AppImage.NotFound; + + public MenuButton BtnShowMenu { get; set; } + public VisibleCheckBox ChkVisible { get; set; } + public WebSearchMenuItem TsiSearch { get; set; } + public ChangeTextMenuItem TsiChangeText { get; set; } + public ChangeCommandMenuItem TsiChangeCommand { get; set; } + public FileLocationMenuItem TsiFileLocation { get; set; } + public FilePropertiesMenuItem TsiFileProperties { get; set; } + public RegLocationMenuItem TsiRegLocation { get; set; } + public RegExportMenuItem TsiRegExport { get; set; } + public DeleteMeMenuItem TsiDeleteMe { get; set; } + readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details); + + private void InitializeComponents() + { + BtnShowMenu = new MenuButton(this); + ChkVisible = new VisibleCheckBox(this); + TsiChangeText = new ChangeTextMenuItem(this); + TsiChangeCommand = new ChangeCommandMenuItem(this); + TsiSearch = new WebSearchMenuItem(this); + TsiFileLocation = new FileLocationMenuItem(this); + TsiFileProperties = new FilePropertiesMenuItem(this); + TsiRegLocation = new RegLocationMenuItem(this); + TsiRegExport = new RegExportMenuItem(this); + TsiDeleteMe = new DeleteMeMenuItem(this); + + ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, + new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); + + TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(), + TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport}); + } + + public void DeleteMe() + { + RegistryEx.DeleteKeyTree(this.RegPath); + RegistryEx.DeleteKeyTree(this.BackupPath); + this.Dispose(); + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/IEList.cs b/ContextMenuManager/Controls/IEList.cs new file mode 100644 index 0000000..9cf269a --- /dev/null +++ b/ContextMenuManager/Controls/IEList.cs @@ -0,0 +1,63 @@ +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + sealed class IEList : MyList + { + public const string IEPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer"; + + public void LoadItems() + { + AddNewItem(); + LoadIEItems(); + } + + private void LoadIEItems() + { + List names = new List(); + using(RegistryKey ieKey = RegistryEx.GetRegistryKey(IEPath)) + { + if(ieKey == null) return; + foreach(string part in IEItem.MeParts) + { + using(RegistryKey meKey = ieKey.OpenSubKey(part)) + { + if(meKey == null) continue; + foreach(string keyName in meKey.GetSubKeyNames()) + { + if(names.Contains(keyName, StringComparer.OrdinalIgnoreCase)) continue; + using(RegistryKey key = meKey.OpenSubKey(keyName)) + { + if(!string.IsNullOrEmpty(key.GetValue("")?.ToString())) + { + this.AddItem(new IEItem(key.Name)); + names.Add(keyName); + } + } + } + } + } + } + } + + private void AddNewItem() + { + NewItem newItem = new NewItem(); + this.AddItem(newItem); + newItem.AddNewItem += (sender, e) => + { + using(NewIEDialog dlg = new NewIEDialog()) + { + if(dlg.ShowDialog() != DialogResult.OK) return; + this.InsertItem(new IEItem(dlg.RegPath), 1); + } + }; + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs b/ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs index 1d5792a..2fb5141 100644 --- a/ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs +++ b/ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System.Windows.Forms; namespace ContextMenuManager.Controls.Interfaces diff --git a/ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs b/ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs index 39e6f55..6e57319 100644 --- a/ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs +++ b/ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Controls; +using BluePointLilac.Controls; namespace ContextMenuManager.Controls.Interfaces { diff --git a/ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs b/ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs index 54990bc..0a2637f 100644 --- a/ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs +++ b/ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs @@ -1,5 +1,6 @@ -using BulePointLilac.Controls; -using static BulePointLilac.Methods.ObjectPath; +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using static BluePointLilac.Methods.ObjectPath; namespace ContextMenuManager.Controls.Interfaces { @@ -15,7 +16,19 @@ sealed class ObjectPathButton : PictureButton public ObjectPathButton(IBtnOpenPathItem item) : base(AppImage.Open) { ((MyListItem)item).AddCtr(this); - this.MouseDown += (sender, e) => ShowPath(item.TargetPath, item.PathType); + this.MouseDown += (sender, e) => + { + switch(item.PathType) + { + case PathType.File: + case PathType.Directory: + ExternalProgram.JumpExplorer(item.TargetPath); + break; + case PathType.Registry: + ExternalProgram.JumpRegEdit(item.TargetPath, null, AppConfig.OpenMoreRegedit); + break; + } + }; } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs b/ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs index 52f0a32..73c408d 100644 --- a/ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs +++ b/ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Controls; +using BluePointLilac.Controls; using System.Windows.Forms; namespace ContextMenuManager.Controls.Interfaces diff --git a/ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs b/ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs index 516b3e2..8d51d7e 100644 --- a/ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs +++ b/ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Controls; +using BluePointLilac.Controls; using System.Windows.Forms; namespace ContextMenuManager.Controls.Interfaces diff --git a/ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs b/ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs index c8ea150..5ee460a 100644 --- a/ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs +++ b/ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs @@ -1,9 +1,10 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using System; using System.Drawing; using System.IO; using System.Windows.Forms; -using static BulePointLilac.Methods.ObjectPath; +using static BluePointLilac.Methods.ObjectPath; namespace ContextMenuManager.Controls.Interfaces { @@ -11,6 +12,7 @@ interface IFoldGroupItem { FoldButton BtnFold { get; set; } bool IsFold { get; set; } + string Text { get; set; } } interface IFoldSubItem @@ -30,10 +32,12 @@ public bool IsFold this.BaseImage = ReplaceImage(value); Control list = ((MyListItem)FoldGroup).Parent; if(list == null) return; + list.SuspendLayout(); foreach(Control ctr in list.Controls) { if(ctr is IFoldSubItem item && item.FoldGroupItem == FoldGroup) ctr.Visible = !value; } + list.ResumeLayout(); } } @@ -53,7 +57,7 @@ public FoldButton(IFoldGroupItem owner, bool fold = false) : base(ReplaceImage(f } } - sealed class GroupPathItem : MyListItem, IFoldGroupItem, IBtnOpenPathItem + class GroupPathItem : MyListItem, IFoldGroupItem, IBtnOpenPathItem { public bool IsFold { @@ -70,6 +74,10 @@ public GroupPathItem(string targetPath, PathType pathType) BtnFold = new FoldButton(this); BtnOpenPath = new ObjectPathButton(this); this.Font = new Font(base.Font, FontStyle.Bold); + if(pathType == PathType.File || pathType == PathType.Directory) + { + targetPath = Environment.ExpandEnvironmentVariables(targetPath); + } this.TargetPath = targetPath; this.PathType = pathType; string tip = null; @@ -90,7 +98,6 @@ public GroupPathItem(string targetPath, PathType pathType) break; } MyToolTip.SetToolTip(BtnOpenPath, tip); - this.SetNoClickEvent(); } public void HideWhenNoSubItem() @@ -102,5 +109,11 @@ public void HideWhenNoSubItem() } if(count == 0) this.Visible = false; } + + protected override void OnDoubleClick(EventArgs e) + { + base.OnDoubleClick(e); + IsFold = !IsFold; + } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs new file mode 100644 index 0000000..7a47b78 --- /dev/null +++ b/ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs @@ -0,0 +1,46 @@ +using BluePointLilac.Methods; +using System.IO; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls.Interfaces +{ + interface ITsiAdministratorItem + { + ContextMenuStrip ContextMenuStrip { get; set; } + RunAsAdministratorItem TsiAdministrator { get; set; } + WshShortcut Shortcut { get; } + } + + sealed class RunAsAdministratorItem : ToolStripMenuItem + { + public RunAsAdministratorItem(ITsiAdministratorItem item) : base(AppString.Menu.RunAsAdministrator) + { + item.ContextMenuStrip.Opening += (sender, e) => + { + if(item.Shortcut == null) + { + this.Enabled = false; + return; + } + string filePath = item.Shortcut.TargetPath; + string extension = Path.GetExtension(filePath)?.ToLower(); + switch(extension) + { + case ".exe": + case ".bat": + case ".cmd": + this.Enabled = true; + break; + default: + this.Enabled = false; + break; + } + this.Checked = item.Shortcut.RunAsAdministrator; + }; + this.Click += (sender, e) => + { + item.Shortcut.RunAsAdministrator = !this.Checked; + }; + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs index dbfc3ec..503fe88 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System.Windows.Forms; namespace ContextMenuManager.Controls.Interfaces diff --git a/ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs index f6c9ef7..e824455 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.IO; using System.Windows.Forms; @@ -13,7 +13,7 @@ interface ITsiDeleteItem interface ITsiRegDeleteItem : ITsiDeleteItem { - string ItemText { get; } + string Text { get; } string RegPath { get; } } @@ -23,20 +23,19 @@ public DeleteMeMenuItem(ITsiDeleteItem item) : base(AppString.Menu.Delete) { this.Click += (sender, e) => { - if(MessageBoxEx.Show(AppString.MessageBox.ConfirmDeletePermanently, - MessageBoxButtons.YesNo) == DialogResult.Yes) + if(item is ITsiRegDeleteItem regItem && AppConfig.AutoBackup) { - if(item is ITsiRegDeleteItem regItem && AppConfig.AutoBackup) - { - string date = DateTime.Today.ToString("yyyy-MM-dd"); - string fileName = ObjectPath.RemoveIllegalChars(regItem.ItemText); - string filePath = $@"{AppConfig.BackupDir}\{date}\{fileName}.reg"; - filePath = ObjectPath.GetNewPathWithIndex(filePath, ObjectPath.PathType.File); - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - RegistryEx.Export(regItem.RegPath, filePath); - } - item.DeleteMe(); + if(MessageBoxEx.Show(AppString.MessageBox.DeleteButCanRestore, + MessageBoxButtons.YesNo) != DialogResult.Yes) return; + string date = DateTime.Today.ToString("yyyy-MM-dd"); + string time = DateTime.Now.ToString("HH.mm.ss"); + string filePath = $@"{AppConfig.BackupDir}\{date}\{regItem.Text}-{time}.reg"; + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + RegistryEx.Export(regItem.RegPath, filePath); } + else if(MessageBoxEx.Show(AppString.MessageBox.ConfirmDeletePermanently, + MessageBoxButtons.YesNo) != DialogResult.Yes) return; + item.DeleteMe(); }; } } diff --git a/ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs index c5e89ff..fbf0c0b 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs @@ -1,7 +1,6 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System.IO; using System.Windows.Forms; -using static BulePointLilac.Methods.ObjectPath; namespace ContextMenuManager.Controls.Interfaces { @@ -17,15 +16,9 @@ sealed class FileLocationMenuItem : ToolStripMenuItem { public FileLocationMenuItem(ITsiFilePathItem item) : base(AppString.Menu.FileLocation) { - bool FileExists() => File.Exists(item.ItemFilePath); - bool DirExists() => Directory.Exists(item.ItemFilePath); - item.ContextMenuStrip.Opening += (sender, e) - => this.Visible = FileExists() || DirExists(); - this.Click += (sender, e) => - { - if(FileExists()) ShowPath(item.ItemFilePath, PathType.File); - else if(DirExists()) ShowPath(item.ItemFilePath, PathType.Directory); - }; + item.ContextMenuStrip.Opening += (sender, e) => + this.Visible = File.Exists(item.ItemFilePath) || Directory.Exists(item.ItemFilePath); + this.Click += (sender, e) => ExternalProgram.JumpExplorer(item.ItemFilePath); } } @@ -35,7 +28,7 @@ public FilePropertiesMenuItem(ITsiFilePathItem item) : base(AppString.Menu.FileP { item.ContextMenuStrip.Opening += (sender, e) => this.Visible = File.Exists(item.ItemFilePath) || Directory.Exists(item.ItemFilePath); - this.Click += (sender, e) => PropertiesDialog.Show(item.ItemFilePath); + this.Click += (sender, e) => ExternalProgram.ShowPropertiesDialog(item.ItemFilePath); } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs index 93ba40d..651b478 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System.Drawing; using System.Windows.Forms; diff --git a/ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs index 64484a6..d8961bc 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.IO; using System.Windows.Forms; diff --git a/ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs index c969cca..01bd228 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs @@ -1,22 +1,21 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System.Windows.Forms; -using static BulePointLilac.Methods.ObjectPath; namespace ContextMenuManager.Controls.Interfaces { interface ITsiRegPathItem { - string RegPath { get; set; } + string RegPath { get; } + string ValueName { get; } ContextMenuStrip ContextMenuStrip { get; set; } RegLocationMenuItem TsiRegLocation { get; set; } } sealed class RegLocationMenuItem : ToolStripMenuItem { - public RegLocationMenuItem(ITsiRegPathItem item) : base(AppString.Menu.RegistryLocation) { - this.Click += (sender, e) => ShowPath(item.RegPath, PathType.Registry); + this.Click += (sender, e) => ExternalProgram.JumpRegEdit(item.RegPath, item.ValueName, AppConfig.OpenMoreRegedit); item.ContextMenuStrip.Opening += (sender, e) => { using(var key = RegistryEx.GetRegistryKey(item.RegPath)) diff --git a/ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs new file mode 100644 index 0000000..6957951 --- /dev/null +++ b/ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs @@ -0,0 +1,138 @@ +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls.Interfaces +{ + interface ITsiShortcutCommandItem + { + WshShortcut Shortcut { get; } + ShortcutCommandMenuItem TsiChangeCommand { get; set; } + ContextMenuStrip ContextMenuStrip { get; set; } + } + + sealed class ShortcutCommandMenuItem : ToolStripMenuItem + { + public ShortcutCommandMenuItem(ITsiShortcutCommandItem item) : base(AppString.Menu.ChangeCommand) + { + item.ContextMenuStrip.Opening += (sender, e) => + { + this.Visible = !string.IsNullOrEmpty(item.Shortcut?.TargetPath); + }; + } + + public bool ChangeCommand(WshShortcut shortcut) + { + using(CommandDialog dlg = new CommandDialog()) + { + dlg.Command = shortcut.TargetPath; + dlg.Arguments = shortcut.Arguments; + if(dlg.ShowDialog() != DialogResult.OK) return false; + shortcut.TargetPath = dlg.Command; + shortcut.Arguments = dlg.Arguments; + shortcut.Save(); + return true; + } + } + + sealed class CommandDialog : CommonDialog + { + public string Command { get; set; } + public string Arguments { get; set; } + + public override void Reset() { } + + protected override bool RunDialog(IntPtr hwndOwner) + { + using(CommandForm frm = new CommandForm()) + { + frm.Command = this.Command; + frm.Arguments = this.Arguments; + bool flag = frm.ShowDialog() == DialogResult.OK; + if(flag) + { + this.Command = frm.Command; + this.Arguments = frm.Arguments; + } + return flag; + } + } + + sealed class CommandForm : ResizbleForm + { + public CommandForm() + { + this.AcceptButton = btnOk; + this.CancelButton = btnCancel; + this.VerticalResizable = false; + this.Font = SystemFonts.MessageBoxFont; + this.Text = AppString.Menu.ChangeCommand; + this.SizeGripStyle = SizeGripStyle.Hide; + this.StartPosition = FormStartPosition.CenterParent; + this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false; + InitializeComponents(); + } + + public string Command + { + get => txtCommand.Text; + set => txtCommand.Text = value; + } + + public string Arguments + { + get => txtArguments.Text; + set => txtArguments.Text = value; + } + + readonly Label lblCommand = new Label + { + Text = AppString.Dialog.ItemCommand, + AutoSize = true + }; + readonly Label lblArguments = new Label + { + Text = AppString.Dialog.CommandArguments, + AutoSize = true + }; + readonly TextBox txtCommand = new TextBox(); + readonly TextBox txtArguments = new TextBox(); + readonly Button btnOk = new Button + { + DialogResult = DialogResult.OK, + Text = AppString.Dialog.Ok, + AutoSize = true + }; + readonly Button btnCancel = new Button + { + DialogResult = DialogResult.Cancel, + Text = AppString.Dialog.Cancel, + AutoSize = true + }; + + private void InitializeComponents() + { + this.Controls.AddRange(new Control[] { lblCommand, lblArguments, txtCommand, txtArguments, btnOk, btnCancel }); + int a = 20.DpiZoom(); + lblArguments.Left = lblCommand.Left = lblCommand.Top = txtCommand.Top = a; + lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a; + btnOk.Top = btnCancel.Top = txtArguments.Bottom + a; + int b = Math.Max(lblCommand.Width, lblArguments.Width) + 3 * a; + this.ClientSize = new Size(250.DpiZoom() + b, btnOk.Bottom + a); + btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top; + btnCancel.Left = this.ClientSize.Width - btnCancel.Width - a; + btnOk.Left = btnCancel.Left - btnOk.Width - a; + this.Resize += (sender, e) => + { + txtArguments.Width = txtCommand.Width = this.ClientSize.Width - b; + txtArguments.Left = txtCommand.Left = btnCancel.Right - txtCommand.Width; + }; + this.OnResize(null); + this.MinimumSize = this.Size; + } + } + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs index daee3dc..5c5e8a6 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System.Windows.Forms; namespace ContextMenuManager.Controls.Interfaces diff --git a/ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs b/ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs index 3f7b1e5..5011b4e 100644 --- a/ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs +++ b/ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using BluePointLilac.Methods; using System.Windows.Forms; namespace ContextMenuManager.Controls.Interfaces @@ -13,14 +13,11 @@ sealed class WebSearchMenuItem : ToolStripMenuItem { public WebSearchMenuItem(ITsiWebSearchItem item) : base(AppString.Menu.WebSearch) { - this.Click += (sender, e) => WebSearch(item.SearchText); - } - - public static void WebSearch(string text) - { - //替换网址转义符 - text = text.Replace("%", "%25").Replace("#", "%23").Replace("&", "%26").Replace("+", "%2B"); - Process.Start(AppConfig.EngineUrl.Replace("%s", text)); + this.Click += (sender, e) => + { + string url = AppConfig.EngineUrl.Replace("%s", item.SearchText); + ExternalProgram.OpenUrl(url); + }; } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/LockNewItem.cs b/ContextMenuManager/Controls/LockNewItem.cs deleted file mode 100644 index 8d939b1..0000000 --- a/ContextMenuManager/Controls/LockNewItem.cs +++ /dev/null @@ -1,103 +0,0 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; -using ContextMenuManager.Controls.Interfaces; -using Microsoft.Win32; -using System; -using System.Security.AccessControl; -using System.Security.Principal; -using System.Windows.Forms; - -namespace ContextMenuManager.Controls -{ - sealed class LockNewItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiWebSearchItem - { - public LockNewItem(ShellNewList list) - { - this.Owner = list; - this.Image = AppImage.Lock; - this.Text = AppString.Item.LockNewMenu; - this.SetNoClickEvent(); - BtnShowMenu = new MenuButton(this); - ChkVisible = new VisibleCheckBox(this) { Checked = IsLocked() }; - MyToolTip.SetToolTip(ChkVisible, AppString.Tip.LockNewMenu); - TsiSearch = new WebSearchMenuItem(this); - this.ContextMenuStrip = new ContextMenuStrip(); - this.ContextMenuStrip.Items.Add(TsiSearch); - } - - public MenuButton BtnShowMenu { get; set; } - public WebSearchMenuItem TsiSearch { get; set; } - public VisibleCheckBox ChkVisible { get; set; } - public ShellNewList Owner { get; private set; } - - public bool ItemVisible - { - get => IsLocked(); - set - { - if(value) Owner.WriteRegistry(); - else UnLock(); - foreach(Control ctr in Owner.Controls) - { - if(ctr.GetType() == typeof(ShellNewItem)) - { - ShellNewItem item = (ShellNewItem)ctr; - if(item.CanSort) - { - item.BtnMoveDown.Visible = item.BtnMoveUp.Visible = value; - } - } - } - } - } - - public string SearchText => Text; - - - public static bool IsLocked() - { - using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath)) - { - RegistrySecurity rs = key.GetAccessControl(); - foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount))) - { - if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase)) - { - if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase)) return true; - } - } - } - return false; - } - - public static void Lock() - { - using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions)) - { - RegistrySecurity rs = new RegistrySecurity(); - RegistryAccessRule rar = new RegistryAccessRule("Everyone", RegistryRights.Delete | RegistryRights.WriteKey, AccessControlType.Deny); - rs.AddAccessRule(rar); - key.SetAccessControl(rs); - } - } - - public static void UnLock() - { - using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions)) - { - RegistrySecurity rs = key.GetAccessControl(); - foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount))) - { - if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase)) - { - if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase)) - { - rs.RemoveAccessRule(rar); - } - } - } - key.SetAccessControl(rs); - } - } - } -} \ No newline at end of file diff --git a/ContextMenuManager/Controls/NewIEDialog.cs b/ContextMenuManager/Controls/NewIEDialog.cs new file mode 100644 index 0000000..fcb39b5 --- /dev/null +++ b/ContextMenuManager/Controls/NewIEDialog.cs @@ -0,0 +1,64 @@ +using BluePointLilac.Methods; +using System; +using System.IO; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + sealed class NewIEDialog : CommonDialog + { + public string RegPath { get; private set; } + public override void Reset() { } + + protected override bool RunDialog(IntPtr hwndOwner) + { + using(NewIEForm frm = new NewIEForm()) + { + bool flag = frm.ShowDialog() == DialogResult.OK; + if(flag) this.RegPath = frm.RegPath; + return flag; + } + } + + sealed class NewIEForm : NewItemForm + { + public string RegPath { get; set; } + + protected override void InitializeComponents() + { + base.InitializeComponents(); + btnOk.Click += (sender, e) => + { + if(ItemText.IsNullOrWhiteSpace()) + { + MessageBoxEx.Show(AppString.MessageBox.TextCannotBeEmpty); + return; + } + if(ItemCommand.IsNullOrWhiteSpace()) + { + MessageBoxEx.Show(AppString.MessageBox.CommandCannotBeEmpty); + return; + } + AddNewItem(); + DialogResult = DialogResult.OK; + }; + + btnBrowse.Click += (sender, e) => + { + using(OpenFileDialog dlg = new OpenFileDialog()) + { + if(dlg.ShowDialog() != DialogResult.OK) return; + this.ItemFilePath = dlg.FileName; + this.ItemText = Path.GetFileNameWithoutExtension(dlg.FileName); + } + }; + } + + private void AddNewItem() + { + this.RegPath = $@"{IEList.IEPath}\{IEItem.MeParts[0]}\{ItemText.Replace("\\", "")}"; + Microsoft.Win32.Registry.SetValue(RegPath, "", ItemCommand); + } + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/NewItem.cs b/ContextMenuManager/Controls/NewItem.cs index 027f81e..039747d 100644 --- a/ContextMenuManager/Controls/NewItem.cs +++ b/ContextMenuManager/Controls/NewItem.cs @@ -1,19 +1,22 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; namespace ContextMenuManager.Controls { class NewItem : MyListItem { - public NewItem() + public NewItem() : this(AppString.Item.NewItem) { } + + public NewItem(string text) { + this.Text = text; this.Image = AppImage.NewItem; - this.Text = AppString.Item.NewItem; this.SetNoClickEvent(); this.AddCtr(BtnAddNewItem); - MyToolTip.SetToolTip(BtnAddNewItem, AppString.Item.NewItem); + MyToolTip.SetToolTip(BtnAddNewItem, text); BtnAddNewItem.MouseDown += (sender, e) => AddNewItem?.Invoke(null, null); + } public event EventHandler AddNewItem; readonly PictureButton BtnAddNewItem = new PictureButton(AppImage.AddNewItem); diff --git a/ContextMenuManager/Controls/NewItemForm.cs b/ContextMenuManager/Controls/NewItemForm.cs index 16e1543..fb65fee 100644 --- a/ContextMenuManager/Controls/NewItemForm.cs +++ b/ContextMenuManager/Controls/NewItemForm.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; using System.Drawing; using System.Windows.Forms; @@ -12,6 +12,7 @@ public NewItemForm() { this.AcceptButton = btnOk; this.CancelButton = btnCancel; + this.Text = AppString.Item.NewItem; this.Font = SystemFonts.MenuFont; this.MaximizeBox = this.MinimizeBox = false; this.ShowIcon = this.ShowInTaskbar = false; @@ -22,15 +23,16 @@ public NewItemForm() } public string ItemText { get => txtText.Text; set => txtText.Text = value; } - public string Command { get => txtCommand.Text; set => txtCommand.Text = value; } + public string ItemFilePath { get => txtFilePath.Text; set => txtFilePath.Text = value; } public string Arguments { get => txtArguments.Text; set => txtArguments.Text = value; } - public string FullCommand + public string ItemCommand { get { - if(Arguments.IsNullOrWhiteSpace()) return Command; - else if(Command.IsNullOrWhiteSpace()) return Arguments; - else return $"\"{Command}\" \"{Arguments}\""; + if(Arguments.IsNullOrWhiteSpace()) return ItemFilePath; + if(ItemFilePath.IsNullOrWhiteSpace()) return Arguments; + if(Arguments.StartsWith("\"") && Arguments.EndsWith("\"")) return $"\"{ItemFilePath}\" {Arguments}"; + return $"\"{ItemFilePath}\" \"{Arguments}\""; } } @@ -50,7 +52,7 @@ public string FullCommand AutoSize = true }; protected readonly TextBox txtText = new TextBox(); - protected readonly TextBox txtCommand = new TextBox(); + protected readonly TextBox txtFilePath = new TextBox(); protected readonly TextBox txtArguments = new TextBox(); protected readonly Button btnBrowse = new Button { @@ -74,12 +76,12 @@ public string FullCommand protected virtual void InitializeComponents() { this.Controls.AddRange(new Control[] { lblText, lblCommand, lblArguments, - txtText, txtCommand, txtArguments, btnBrowse, btnOk, btnCancel }); + txtText, txtFilePath, txtArguments, btnBrowse, btnOk, btnCancel }); int a = 20.DpiZoom(); btnBrowse.Anchor = btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top; txtText.Top = lblText.Top = lblText.Left = lblCommand.Left = lblArguments.Left = a; - btnBrowse.Top = txtCommand.Top = lblCommand.Top = txtText.Bottom + a; - lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a; + btnBrowse.Top = txtFilePath.Top = lblCommand.Top = txtText.Bottom + a; + lblArguments.Top = txtArguments.Top = txtFilePath.Bottom + a; btnOk.Top = btnCancel.Top = txtArguments.Bottom + a; btnCancel.Left = btnBrowse.Left = this.ClientSize.Width - btnCancel.Width - a; btnOk.Left = btnCancel.Left - btnOk.Width - a; @@ -88,8 +90,8 @@ protected virtual void InitializeComponents() this.MinimumSize = this.Size; this.Resize += (sender, e) => { - txtText.Width = txtCommand.Width = txtArguments.Width = this.ClientSize.Width - b; - txtText.Left = txtCommand.Left = txtArguments.Left = btnBrowse.Left - txtCommand.Width - a; + txtText.Width = txtFilePath.Width = txtArguments.Width = this.ClientSize.Width - b; + txtText.Left = txtFilePath.Left = txtArguments.Left = btnBrowse.Left - txtFilePath.Width - a; LastSize = this.Size; }; if(LastSize != null) this.Size = LastSize; diff --git a/ContextMenuManager/Controls/NewSendToDialog.cs b/ContextMenuManager/Controls/NewLnkFileDialog.cs similarity index 51% rename from ContextMenuManager/Controls/NewSendToDialog.cs rename to ContextMenuManager/Controls/NewLnkFileDialog.cs index baecfa2..54ed9ee 100644 --- a/ContextMenuManager/Controls/NewSendToDialog.cs +++ b/ContextMenuManager/Controls/NewLnkFileDialog.cs @@ -1,28 +1,37 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.IO; using System.Windows.Forms; namespace ContextMenuManager.Controls { - sealed class NewSendToDialog : CommonDialog + sealed class NewLnkFileDialog : CommonDialog { - public string FilePath { get; private set; } + public string ItemText { get; set; } + public string ItemFilePath { get; set; } + public string Arguments { get; set; } + public string FileFilter { get; set; } public override void Reset() { } protected override bool RunDialog(IntPtr hwndOwner) { - using(NewSendToForm frm = new NewSendToForm()) + using(NewLnkForm frm = new NewLnkForm()) { + frm.FileFilter = this.FileFilter; bool flag = frm.ShowDialog() == DialogResult.OK; - if(flag) this.FilePath = frm.FilePath; + if(flag) + { + this.ItemText = frm.ItemText; + this.ItemFilePath = frm.ItemFilePath; + this.Arguments = frm.Arguments; + } return flag; } } - sealed class NewSendToForm : NewItemForm + sealed class NewLnkForm : NewItemForm { - public string FilePath { get; set; } + public string FileFilter { get; set; } readonly RadioButton rdoFile = new RadioButton { @@ -39,7 +48,6 @@ sealed class NewSendToForm : NewItemForm protected override void InitializeComponents() { base.InitializeComponents(); - this.Text = AppString.Dialog.NewSendToItem; this.Controls.AddRange(new Control[] { rdoFile, rdoFolder }); rdoFile.Top = rdoFolder.Top = btnOk.Top; rdoFile.Left = lblCommand.Left; @@ -56,25 +64,34 @@ protected override void InitializeComponents() if(ItemText.IsNullOrWhiteSpace()) { MessageBoxEx.Show(AppString.MessageBox.TextCannotBeEmpty); - return; } - if(Command.IsNullOrWhiteSpace()) + else if(ItemFilePath.IsNullOrWhiteSpace()) { MessageBoxEx.Show(AppString.MessageBox.CommandCannotBeEmpty); - return; } - if(rdoFile.Checked && !ObjectPath.GetFullFilePath(Command, out _)) + else if(rdoFile.Checked && !ObjectPath.GetFullFilePath(ItemFilePath, out _)) { MessageBoxEx.Show(AppString.MessageBox.FileNotExists); - return; } - if(rdoFolder.Checked && !Directory.Exists(Command)) + else if(rdoFolder.Checked && !Directory.Exists(ItemFilePath)) { MessageBoxEx.Show(AppString.MessageBox.FolderNotExists); - return; } - AddNewItem(); - DialogResult = DialogResult.OK; + else DialogResult = DialogResult.OK; + }; + + txtFilePath.TextChanged += (sender, e) => + { + if(Path.GetExtension(ItemFilePath).ToLower() == ".lnk") + { + using(WshShortcut shortcut = new WshShortcut(ItemFilePath)) + { + if(File.Exists(shortcut.TargetPath)) + { + ItemFilePath = shortcut.TargetPath; + } + } + } }; } @@ -82,10 +99,23 @@ private void BrowseFile() { using(OpenFileDialog dlg = new OpenFileDialog()) { - dlg.Filter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.vbs;*.vbe;*.jse;*.wsf"; + dlg.Filter = this.FileFilter; + //取消获取lnk目标路径,可选中UWP快捷方式 + dlg.DereferenceLinks = false; if(dlg.ShowDialog() == DialogResult.OK) { - Command = dlg.FileName; + ItemFilePath = dlg.FileName; + string extension = Path.GetExtension(dlg.FileName).ToLower(); + if(extension == ".lnk") + { + using(WshShortcut shortcut = new WshShortcut(dlg.FileName)) + { + if(File.Exists(shortcut.TargetPath)) + { + ItemFilePath = shortcut.TargetPath; + } + } + } ItemText = Path.GetFileNameWithoutExtension(dlg.FileName); } } @@ -95,30 +125,15 @@ private void BrowseFolder() { using(FolderBrowserDialog dlg = new FolderBrowserDialog()) { - if(Directory.Exists(Command)) dlg.SelectedPath = Command; + if(Directory.Exists(ItemFilePath)) dlg.SelectedPath = ItemFilePath; else dlg.SelectedPath = Application.StartupPath; if(dlg.ShowDialog() == DialogResult.OK) { - Command = dlg.SelectedPath; - ItemText = new DirectoryInfo(dlg.SelectedPath).Name; + ItemFilePath = dlg.SelectedPath; + ItemText = Path.GetFileNameWithoutExtension(dlg.SelectedPath); } } } - - private void AddNewItem() - { - FilePath = $@"{SendToList.SendToPath}\{ObjectPath.RemoveIllegalChars(ItemText)}.lnk"; - FilePath = ObjectPath.GetNewPathWithIndex(FilePath, ObjectPath.PathType.File); - WshShortcut shortcut = new WshShortcut - { - FullName = FilePath, - TargetPath = Command, - WorkingDirectory = Path.GetDirectoryName(Command), - Arguments = Arguments - }; - shortcut.Save(); - SendToList.DesktopIniWriter.SetValue("LocalizedFileNames", Path.GetFileName(FilePath), ItemText); - } } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/NewOpenWithDialog.cs b/ContextMenuManager/Controls/NewOpenWithDialog.cs index ab57186..9de58a1 100644 --- a/ContextMenuManager/Controls/NewOpenWithDialog.cs +++ b/ContextMenuManager/Controls/NewOpenWithDialog.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.Diagnostics; using System.IO; @@ -26,12 +26,13 @@ sealed class NewOpenWithForm : NewItemForm public string RegPath { get; private set; } private string FilePath; - private string AppRegPath; + private string FileName => Path.GetFileName(FilePath); + private string AppRegPath => $@"HKEY_CLASSES_ROOT\Applications\{FileName}"; + private string CommandPath => $@"{AppRegPath}\shell\open\command"; protected override void InitializeComponents() { base.InitializeComponents(); - this.Text = AppString.Dialog.NewOpenWithItem; btnBrowse.Click += (sender, e) => BrowseFile(); btnOk.Click += (sender, e) => { @@ -40,17 +41,26 @@ protected override void InitializeComponents() MessageBoxEx.Show(AppString.MessageBox.TextCannotBeEmpty); return; } - if(FullCommand.IsNullOrWhiteSpace()) + if(ItemCommand.IsNullOrWhiteSpace()) { MessageBoxEx.Show(AppString.MessageBox.CommandCannotBeEmpty); return; } - FilePath = ObjectPath.ExtractFilePath(Command); - AppRegPath = $@"HKEY_CLASSES_ROOT\Applications\{Path.GetFileName(FilePath)}"; - if(FilePath == null || RegistryEx.GetRegistryKey(AppRegPath) != null) + FilePath = ObjectPath.ExtractFilePath(base.ItemFilePath); + using(var key = RegistryEx.GetRegistryKey(CommandPath)) { - MessageBoxEx.Show(AppString.MessageBox.UnsupportedFilename); - return; + string path = ObjectPath.ExtractFilePath(key?.GetValue("")?.ToString()); + string name = Path.GetFileName(path); + if(FilePath != null && FilePath.Equals(path, StringComparison.OrdinalIgnoreCase)) + { + MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded); + return; + } + if(FileName == null || FileName.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + MessageBoxEx.Show(AppString.MessageBox.UnsupportedFilename); + return; + } } AddNewItem(); this.DialogResult = DialogResult.OK; @@ -64,8 +74,8 @@ private void BrowseFile() dlg.Filter = $"{AppString.Dialog.Program}|*.exe"; if(dlg.ShowDialog() == DialogResult.OK) { - Command = dlg.FileName; - Arguments = "%1"; + base.ItemFilePath = dlg.FileName; + Arguments = "\"%1\""; ItemText = FileVersionInfo.GetVersionInfo(dlg.FileName).FileDescription; } } @@ -76,11 +86,11 @@ private void AddNewItem() using(var key = RegistryEx.GetRegistryKey(AppRegPath, true, true)) { key.SetValue("FriendlyAppName", ItemText); - using(var cmdKey = key.CreateSubKey(@"shell\open\command", true)) - { - cmdKey.SetValue("", FullCommand); - RegPath = cmdKey.Name; - } + } + using(var cmdKey = RegistryEx.GetRegistryKey(CommandPath, true, true)) + { + cmdKey.SetValue("", ItemCommand); + RegPath = cmdKey.Name; } } } diff --git a/ContextMenuManager/Controls/NewShellDialog.cs b/ContextMenuManager/Controls/NewShellDialog.cs index de0fa57..66d4461 100644 --- a/ContextMenuManager/Controls/NewShellDialog.cs +++ b/ContextMenuManager/Controls/NewShellDialog.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; using System.IO; using System.Windows.Forms; @@ -45,12 +45,11 @@ sealed class NewShellForm : NewItemForm Text = AppString.Dialog.MultiMenu, AutoSize = true }; + readonly ShellExecuteCheckBox chkSE = new ShellExecuteCheckBox(); static readonly string[] DirScenePaths = { ShellList.MENUPATH_DIRECTORY, - ShellList.MENUPATH_DIRECTORY_IMAGE, - ShellList.MENUPATH_DIRECTORY_VIDEO, - ShellList.MENUPATH_DIRECTORY_AUDIO + $@"{ShellList.SYSFILEASSPATH}\Directory." }; static readonly string[] FileObjectsScenePaths = { ShellList.MENUPATH_FILE, @@ -58,19 +57,19 @@ sealed class NewShellForm : NewItemForm ShellList.MENUPATH_ALLOBJECTS, ShellList.SYSFILEASSPATH, ShellList.MENUPATH_UNKNOWN, - ShellList.MENUPATH_LNKFILE, - ShellList.MENUPATH_EXEFILE, ShellList.MENUPATH_UWPLNK }; protected override void InitializeComponents() { base.InitializeComponents(); - this.Text = AppString.Dialog.NewShellItem; - this.Controls.AddRange(new[] { rdoSingle, rdoMulti }); + this.Controls.AddRange(new Control[] { rdoSingle, rdoMulti, chkSE }); rdoSingle.Top = rdoMulti.Top = btnOk.Top; rdoSingle.Left = lblCommand.Left; rdoMulti.Left = rdoSingle.Right + 20.DpiZoom(); + chkSE.Top = txtArguments.Top + (txtArguments.Height - chkSE.Height) / 2; + this.Resize += (sender, e) => chkSE.Left = txtArguments.Right + 20.DpiZoom(); + this.OnResize(null); rdoMulti.CheckedChanged += (sender, e) => { @@ -80,8 +79,8 @@ protected override void InitializeComponents() rdoSingle.Checked = true; return; } - lblCommand.Enabled = txtCommand.Enabled = lblArguments.Enabled - = txtArguments.Enabled = btnBrowse.Enabled = !rdoMulti.Checked; + lblCommand.Enabled = txtFilePath.Enabled = lblArguments.Enabled + = txtArguments.Enabled = btnBrowse.Enabled = chkSE.Enabled = !rdoMulti.Checked; }; btnBrowse.Click += (sender, e) => BrowseFile(); @@ -104,19 +103,36 @@ private void BrowseFile() { using(OpenFileDialog dlg = new OpenFileDialog()) { - dlg.Filter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.pif;*.com"; + dlg.Filter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.pif;*.com;*.vbs;*.vbe;*.js;*.jse;*.wsf"; if(dlg.ShowDialog() != DialogResult.OK) return; - Command = dlg.FileName; ItemText = Path.GetFileNameWithoutExtension(dlg.FileName); + string extension = Path.GetExtension(dlg.FileName).ToLower(); + switch(extension) + { + case ".vbs": + case ".vbe": + case ".js": + case ".jse": + case ".wsf": + ItemFilePath = "wscript.exe"; + Arguments = dlg.FileName; + break; + default: + ItemFilePath = dlg.FileName; + break; + } if(Array.FindIndex(DirScenePaths, path - => ScenePath.Equals(path, StringComparison.OrdinalIgnoreCase)) != -1) + => ScenePath.StartsWith(path, StringComparison.OrdinalIgnoreCase)) != -1) { - Arguments = "%V";//自动加目录后缀 + if(!Arguments.IsNullOrWhiteSpace()) Arguments += " "; + if(ScenePath != ShellList.MENUPATH_BACKGROUND) + Arguments += "\"%V\"";//自动加目录后缀 } else if(Array.FindIndex(FileObjectsScenePaths, path => ScenePath.StartsWith(path, StringComparison.OrdinalIgnoreCase)) != -1) { - Arguments += "%1";//自动加文件对象后缀 + if(!Arguments.IsNullOrWhiteSpace()) Arguments += " "; + Arguments += "\"%1\"";//自动加文件对象后缀 } } } @@ -126,7 +142,7 @@ private void AddNewItem() using(var shellKey = RegistryEx.GetRegistryKey(ShellPath, true, true)) { string keyName = "Item"; - NewItemRegPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry); + NewItemRegPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry, 0); keyName = RegistryEx.GetKeyName(NewItemRegPath); using(var key = shellKey.CreateSubKey(keyName, true)) @@ -136,8 +152,13 @@ private void AddNewItem() key.SetValue("SubCommands", ""); else { - if(!FullCommand.IsNullOrWhiteSpace()) - key.CreateSubKey("command", true).SetValue("", FullCommand); + if(!ItemCommand.IsNullOrWhiteSpace()) + { + string command; + if(!chkSE.Checked) command = ItemCommand; + else command = ShellExecuteDialog.GetCommand(ItemFilePath, Arguments, chkSE.Verb, chkSE.WindowStyle); + key.CreateSubKey("command", true).SetValue("", command); + } } } } diff --git a/ContextMenuManager/Controls/OpenWithItem.cs b/ContextMenuManager/Controls/OpenWithItem.cs index f75cd0f..bcfabd3 100644 --- a/ContextMenuManager/Controls/OpenWithItem.cs +++ b/ContextMenuManager/Controls/OpenWithItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; @@ -33,8 +33,9 @@ public string RegPath ChkVisible.Checked = this.ItemVisible; } } - - private string AppPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegPath))); + public string ValueName => null; + private string ShellPath => RegistryEx.GetParentPath(RegPath); + private string AppPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ShellPath)); private bool NameEquals => RegistryEx.GetKeyName(AppPath).Equals(Path.GetFileName(ItemFilePath), StringComparison.OrdinalIgnoreCase); private Icon ItemIcon => Icon.ExtractAssociatedIcon(ItemFilePath); @@ -123,6 +124,10 @@ private void InitializeComponents() public void DeleteMe() { RegistryEx.DeleteKeyTree(this.RegPath); + using(RegistryKey key = RegistryEx.GetRegistryKey(ShellPath)) + { + if(key.GetSubKeyNames().Length == 0) RegistryEx.DeleteKeyTree(this.AppPath); + } this.Dispose(); } } diff --git a/ContextMenuManager/Controls/OpenWithList.cs b/ContextMenuManager/Controls/OpenWithList.cs index 8d91282..131b287 100644 --- a/ContextMenuManager/Controls/OpenWithList.cs +++ b/ContextMenuManager/Controls/OpenWithList.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using Microsoft.Win32; using System; using System.Collections.Generic; @@ -12,10 +12,10 @@ sealed class OpenWithList : MyList { public void LoadItems() { - this.LoadCommonItems(); + this.LoadOpenWithItems(); this.SortItemByText(); this.AddNewItem(); - RegRuleItem storeItem = new RegRuleItem(RegRuleItem.UseStoreOpenWith) + VisibleRegRuleItem storeItem = new VisibleRegRuleItem(VisibleRegRuleItem.UseStoreOpenWith) { //Win8、Win8.1、Win10才有在应用商店中查找应用 Visible = WindowsOsVersion.ISAfterOrEqual8 @@ -23,7 +23,7 @@ public void LoadItems() this.InsertItem(storeItem, 1); } - private void LoadCommonItems() + private void LoadOpenWithItems() { using(RegistryKey appKey = Registry.ClassesRoot.OpenSubKey("Applications")) { diff --git a/ContextMenuManager/Controls/RegRuleItem.cs b/ContextMenuManager/Controls/RegRuleItem.cs deleted file mode 100644 index a0450a4..0000000 --- a/ContextMenuManager/Controls/RegRuleItem.cs +++ /dev/null @@ -1,230 +0,0 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; -using ContextMenuManager.Controls.Interfaces; -using Microsoft.Win32; -using System.Drawing; -using System.Windows.Forms; - -namespace ContextMenuManager.Controls -{ - sealed class RegRuleItem : MyListItem, IChkVisibleItem, IFoldSubItem, IBtnShowMenuItem, ITsiWebSearchItem - { - public struct RegRule - { - public string RegPath { get; set; } - public string ValueName { get; set; } - public RegistryValueKind ValueKind { get; set; } - public object TurnOnValue { get; set; } - public object TurnOffValue { get; set; } - public RegRule(string regPath, string valueName, object turnOnValue, - object turnOffValue, RegistryValueKind valueKind = RegistryValueKind.DWord) - { - this.RegPath = regPath; this.ValueName = valueName; - this.TurnOnValue = turnOnValue; this.TurnOffValue = turnOffValue; - this.ValueKind = valueKind; - } - } - - public struct ItemInfo - { - public string Text { get; set; } - public Image Image { get; set; } - public string Tip { get; set; } - public bool RestartExplorer { get; set; } - } - - public struct RuleAndInfo - { - public RegRule[] Rules { get; set; } - public ItemInfo ItemInfo { get; set; } - } - - private RegRuleItem(ItemInfo info) - { - this.Text = info.Text; - this.Image = info.Image; - this.RestartExplorer = info.RestartExplorer; - BtnShowMenu = new MenuButton(this); - ChkVisible = new VisibleCheckBox(this); - MyToolTip.SetToolTip(ChkVisible, info.Tip); - TsiSearch = new WebSearchMenuItem(this); - this.ContextMenuStrip = new ContextMenuStrip(); - this.ContextMenuStrip.Items.Add(TsiSearch); - } - - public RegRuleItem(RegRule[] rules, ItemInfo info) - : this(info) { this.Rules = rules; } - - public RegRuleItem(RegRule rule, ItemInfo info) - : this(info) { this.Rules = new[] { rule }; } - - public RegRuleItem(RuleAndInfo ruleAndInfo) - : this(ruleAndInfo.Rules, ruleAndInfo.ItemInfo) { } - - private RegRule[] _Rules; - public RegRule[] Rules - { - get => _Rules; - set - { - _Rules = value; - ChkVisible.Checked = ItemVisible; - } - } - - public VisibleCheckBox ChkVisible { get; set; } - public bool RestartExplorer { get; set; } - - public bool ItemVisible - { - get - { - foreach(RegRule rule in Rules) - { - using(RegistryKey key = RegistryEx.GetRegistryKey(rule.RegPath)) - { - if(key?.GetValue(rule.ValueName) == null) continue; - if(key.GetValueKind(rule.ValueName) != rule.ValueKind) continue; - if(key.GetValue(rule.ValueName).ToString().ToLower() - == rule.TurnOffValue.ToString().ToLower()) return false; - } - } - return true; - } - set - { - foreach(RegRule rule in Rules) - { - object data = value ? rule.TurnOnValue : rule.TurnOffValue; - if(data != null) - { - Registry.SetValue(rule.RegPath, rule.ValueName, data, rule.ValueKind); - } - else - { - RegistryEx.DeleteValue(rule.RegPath, rule.ValueName); - } - } - if(RestartExplorer) ExplorerRestarter.NeedRestart = true; - } - } - - public IFoldGroupItem FoldGroupItem { get; set; } - public WebSearchMenuItem TsiSearch { get; set; } - public MenuButton BtnShowMenu { get; set; } - - public string SearchText => Text; - - const string CU_SMWCEA = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced"; - const string LM_SMWCPE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"; - const string CU_SMWCPE = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"; - const string LM_SMWCE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer"; - const string CU_SMWCE = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer"; - const string LM_SPMWE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Explorer"; - const string CU_SPMWE = @"HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Explorer"; - public const string SkypeGuidStr = "{776dbc8d-7347-478c-8d71-791e12ef49d8}"; - - public static RuleAndInfo CustomFolder = new RuleAndInfo - { - Rules = new[] { - new RegRule(LM_SMWCPE, "NoCustomizeThisFolder", 0, 1), - new RegRule(LM_SMWCPE, "NoCustomizeWebView", 0, 1), - new RegRule(CU_SMWCPE, "NoCustomizeThisFolder", 0, 1), - new RegRule(CU_SMWCPE, "NoCustomizeWebView", 0, 1) - }, - ItemInfo = new ItemInfo - { - Text = AppString.Item.CustomFolder, - Image = AppImage.Folder, - Tip = AppString.Tip.CustomFolder, - RestartExplorer = true - } - }; - - public static RuleAndInfo NetworkDrive = new RuleAndInfo - { - Rules = new[] { - new RegRule(LM_SMWCPE, "NoNetConnectDisconnect", 0, 1), - new RegRule(CU_SMWCPE, "NoNetConnectDisconnect", 0, 1) - }, - ItemInfo = new ItemInfo - { - Text = $"{AppString.Item.MapNetworkDrive} && {AppString.Item.DisconnectNetworkDrive}", - Image = AppImage.NetworkDrive, - RestartExplorer = true - } - }; - - public static RuleAndInfo RecycleBinProperties = new RuleAndInfo - { - Rules = new[] { - new RegRule(LM_SMWCPE, "NoPropertiesRecycleBin", 0, 1), - new RegRule(CU_SMWCPE, "NoPropertiesRecycleBin", 0, 1) - }, - ItemInfo = new ItemInfo - { - Text = AppString.Item.RecycleBinProperties, - Image = AppImage.RecycleBin, - RestartExplorer = true - } - }; - - public static RuleAndInfo SendToDrive = new RuleAndInfo - { - Rules = new[] { - new RegRule(LM_SMWCPE, "NoDrivesInSendToMenu", 0, 1), - new RegRule(CU_SMWCPE, "NoDrivesInSendToMenu", 0, 1) - }, - ItemInfo = new ItemInfo - { - Text = AppString.Item.RemovableDrive, - Image = AppImage.Drive, - Tip = AppString.Tip.SendToDrive, - RestartExplorer = true - } - }; - - public static RuleAndInfo DeferBuildSendTo = new RuleAndInfo - { - Rules = new[] { - new RegRule(LM_SMWCE, "DelaySendToMenuBuild", 0, 1), - new RegRule(CU_SMWCE, "DelaySendToMenuBuild", 0, 1) - }, - ItemInfo = new ItemInfo - { - Text = AppString.Item.BuildSendtoMenu, - Image = AppImage.SendTo, - Tip = AppString.Tip.BuildSendtoMenu - } - }; - - public static RuleAndInfo UseStoreOpenWith = new RuleAndInfo - { - Rules = new[] { - new RegRule(LM_SPMWE, "NoUseStoreOpenWith", 0, 1), - new RegRule(CU_SPMWE, "NoUseStoreOpenWith", 0, 1) - }, - ItemInfo = new ItemInfo - { - Text = AppString.Item.UseStoreOpenWith, - Image = AppImage.MicrosoftStore - } - }; - - public static RuleAndInfo ShareWithSkype = new RuleAndInfo - { - Rules = new[] - { - new RegRule(GuidBlockedItem.HKLMBLOCKED, SkypeGuidStr, null, "", RegistryValueKind.String), - new RegRule(GuidBlockedItem.HKCUBLOCKED, SkypeGuidStr, null, "", RegistryValueKind.String) - }, - ItemInfo = new ItemInfo - { - Text = AppString.Item.ShareWithSkype, - Tip = AppString.Tip.ShareWithSkype, - Image = AppImage.Skype, - RestartExplorer = true - } - }; - } -} \ No newline at end of file diff --git a/ContextMenuManager/Controls/RuleItem.cs b/ContextMenuManager/Controls/RuleItem.cs new file mode 100644 index 0000000..1a0130c --- /dev/null +++ b/ContextMenuManager/Controls/RuleItem.cs @@ -0,0 +1,498 @@ +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using ContextMenuManager.Controls.Interfaces; +using Microsoft.Win32; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + class RuleItem : MyListItem, IFoldSubItem, IBtnShowMenuItem, ITsiWebSearchItem + { + public RuleItem(ItemInfo info) + { + this.Text = info.Text; + this.Image = info.Image; + this.RestartExplorer = info.RestartExplorer; + BtnShowMenu = new MenuButton(this); + TsiSearch = new WebSearchMenuItem(this); + this.ContextMenuStrip = new ContextMenuStrip(); + this.ContextMenuStrip.Items.Add(TsiSearch); + } + + public IFoldGroupItem FoldGroupItem { get; set; } + public WebSearchMenuItem TsiSearch { get; set; } + public MenuButton BtnShowMenu { get; set; } + + public bool RestartExplorer { get; set; } + + public string SearchText + { + get + { + if(this.FoldGroupItem == null) return this.Text; + else return $"{FoldGroupItem.Text} {this.Text}"; + } + } + } + + public struct ItemInfo + { + public string Text { get; set; } + public Image Image { get; set; } + public string Tip { get; set; } + public bool RestartExplorer { get; set; } + } + + sealed class VisibleRegRuleItem : RuleItem, IChkVisibleItem, ITsiRegPathItem + { + public struct RegRule + { + public string RegPath { get; set; } + public string ValueName { get; set; } + public RegistryValueKind ValueKind { get; set; } + public object TurnOnValue { get; set; } + public object TurnOffValue { get; set; } + public RegRule(string regPath, string valueName, object turnOnValue, + object turnOffValue, RegistryValueKind valueKind = RegistryValueKind.DWord) + { + this.RegPath = regPath; this.ValueName = valueName; + this.TurnOnValue = turnOnValue; this.TurnOffValue = turnOffValue; + this.ValueKind = valueKind; + } + } + + public struct RuleAndInfo + { + public RegRule[] Rules { get; set; } + public ItemInfo ItemInfo { get; set; } + } + + private VisibleRegRuleItem(ItemInfo info) : base(info) + { + ChkVisible = new VisibleCheckBox(this); + MyToolTip.SetToolTip(ChkVisible, info.Tip); + TsiRegLocation = new RegLocationMenuItem(this); + this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { new ToolStripSeparator(), TsiRegLocation }); + } + + public VisibleRegRuleItem(RegRule[] rules, ItemInfo info) + : this(info) { this.Rules = rules; } + + public VisibleRegRuleItem(RegRule rule, ItemInfo info) + : this(info) { this.Rules = new[] { rule }; } + + public VisibleRegRuleItem(RuleAndInfo ruleAndInfo) + : this(ruleAndInfo.Rules, ruleAndInfo.ItemInfo) { } + + private RegRule[] _Rules; + public RegRule[] Rules + { + get => _Rules; + set + { + _Rules = value; + ChkVisible.Checked = ItemVisible; + } + } + + public VisibleCheckBox ChkVisible { get; set; } + public RegLocationMenuItem TsiRegLocation { get; set; } + + public bool ItemVisible + { + get + { + for(int i = 0; i < Rules.Length; i++) + { + RegRule rule = Rules[i]; + using(RegistryKey key = RegistryEx.GetRegistryKey(rule.RegPath)) + { + string value = key?.GetValue(rule.ValueName)?.ToString().ToLower(); + string turnOnValue = rule.TurnOnValue?.ToString().ToLower(); + string turnOffValue = rule.TurnOffValue?.ToString().ToLower(); + if(value == null || key.GetValueKind(rule.ValueName) != rule.ValueKind) + { + if(i < Rules.Length - 1) continue; + } + if(value == turnOnValue) return true; + if(value == turnOffValue) return false; + } + } + return true; + } + set + { + foreach(RegRule rule in Rules) + { + object data = value ? rule.TurnOnValue : rule.TurnOffValue; + if(data != null) + { + Registry.SetValue(rule.RegPath, rule.ValueName, data, rule.ValueKind); + } + else + { + RegistryEx.DeleteValue(rule.RegPath, rule.ValueName); + } + } + if(RestartExplorer) ExplorerRestarter.Show(); + } + } + + public string RegPath => Rules[0].RegPath; + public string ValueName => Rules[0].ValueName; + + const string LM_SMWCPE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer"; + const string CU_SMWCPE = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer"; + const string LM_SMWCE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer"; + const string CU_SMWCE = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer"; + const string LM_SPMWE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Explorer"; + const string CU_SPMWE = @"HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Explorer"; + public const string SkypeGuid = "{776dbc8d-7347-478c-8d71-791e12ef49d8}"; + + public static RuleAndInfo CustomFolder = new RuleAndInfo + { + Rules = new[] { + new RegRule(LM_SMWCPE, "NoCustomizeThisFolder", null, 1), + new RegRule(LM_SMWCPE, "NoCustomizeWebView", null, 1), + new RegRule(CU_SMWCPE, "NoCustomizeThisFolder", null, 1), + new RegRule(CU_SMWCPE, "NoCustomizeWebView", null, 1) + }, + ItemInfo = new ItemInfo + { + Text = AppString.Item.CustomFolder, + Image = AppImage.Folder, + Tip = AppString.Tip.CustomFolder, + RestartExplorer = true + } + }; + + public static RuleAndInfo NetworkDrive = new RuleAndInfo + { + Rules = new[] { + new RegRule(LM_SMWCPE, "NoNetConnectDisconnect", null, 1), + new RegRule(CU_SMWCPE, "NoNetConnectDisconnect", null, 1) + }, + ItemInfo = new ItemInfo + { + Text = $"{AppString.Item.MapNetworkDrive} && {AppString.Item.DisconnectNetworkDrive}", + Image = AppImage.NetworkDrive, + RestartExplorer = true + } + }; + + public static RuleAndInfo RecycleBinProperties = new RuleAndInfo + { + Rules = new[] { + new RegRule(LM_SMWCPE, "NoPropertiesRecycleBin", null, 1), + new RegRule(CU_SMWCPE, "NoPropertiesRecycleBin", null, 1) + }, + ItemInfo = new ItemInfo + { + Text = AppString.Item.RecycleBinProperties, + Image = AppImage.RecycleBin, + RestartExplorer = true + } + }; + + public static RuleAndInfo SendToDrive = new RuleAndInfo + { + Rules = new[] { + new RegRule(LM_SMWCPE, "NoDrivesInSendToMenu", null, 1), + new RegRule(CU_SMWCPE, "NoDrivesInSendToMenu", null, 1) + }, + ItemInfo = new ItemInfo + { + Text = AppString.Item.RemovableDrive, + Image = AppImage.Drive, + Tip = AppString.Tip.SendToDrive, + RestartExplorer = true + } + }; + + public static RuleAndInfo DeferBuildSendTo = new RuleAndInfo + { + Rules = new[] { + new RegRule(LM_SMWCE, "DelaySendToMenuBuild", null, 1), + new RegRule(CU_SMWCE, "DelaySendToMenuBuild", null, 1) + }, + ItemInfo = new ItemInfo + { + Text = AppString.Item.BuildSendtoMenu, + Image = AppImage.SendTo, + Tip = AppString.Tip.BuildSendtoMenu + } + }; + + public static RuleAndInfo UseStoreOpenWith = new RuleAndInfo + { + Rules = new[] { + new RegRule(LM_SPMWE, "NoUseStoreOpenWith", null, 1), + new RegRule(CU_SPMWE, "NoUseStoreOpenWith", null, 1) + }, + ItemInfo = new ItemInfo + { + Text = AppString.Item.UseStoreOpenWith, + Image = AppImage.MicrosoftStore + } + }; + + public static RuleAndInfo ShareWithSkype = new RuleAndInfo + { + Rules = new[] + { + new RegRule(GuidBlockedItem.HKLMBLOCKED, SkypeGuid, null, "", RegistryValueKind.String), + new RegRule(GuidBlockedItem.HKCUBLOCKED, SkypeGuid, null, "", RegistryValueKind.String) + }, + ItemInfo = new ItemInfo + { + Text = AppString.Item.ShareWithSkype, + Image = AppImage.Skype, + RestartExplorer = true + } + }; + } + + sealed class NumberRegRuleItem : RuleItem, ITsiRegPathItem + { + public struct RegRule + { + public string RegPath { get; set; } + public string ValueName { get; set; } + public RegistryValueKind ValueKind { get; set; } + public int MaxValue { get; set; } + public int MinValue { get; set; } + public int DefaultValue { get; set; } + } + + readonly NumericUpDown NudValue = new NumericUpDown + { + Font = new Font(SystemFonts.MenuFont.FontFamily, 12F), + TextAlign = HorizontalAlignment.Center, + Width = 80.DpiZoom() + }; + public RegLocationMenuItem TsiRegLocation { get; set; } + + public NumberRegRuleItem(RegRule rule, ItemInfo info) : base(info) + { + this.AddCtr(NudValue); + MyToolTip.SetToolTip(NudValue, info.Tip); + TsiRegLocation = new RegLocationMenuItem(this); + this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { new ToolStripSeparator(), TsiRegLocation }); + this.Rule = rule; + NudValue.Maximum = rule.MaxValue; + NudValue.Minimum = rule.MinValue; + NudValue.ValueChanged += (sender, e) => + { + if(NudValue.Value == Rule.DefaultValue) NudValue.ForeColor = Color.Red; + else NudValue.ForeColor = Color.Black; + this.ItemValue = (int)NudValue.Value; + }; + NudValue.Value = ItemValue; + } + + public string RegPath => Rule.RegPath; + public string ValueName => Rule.ValueName; + public RegRule Rule { get; set; } + + public int ItemValue + { + get + { + object value = Registry.GetValue(Rule.RegPath, Rule.ValueName, null); + if(value == null) return Rule.DefaultValue; + int num = Convert.ToInt32(value); + if(num > Rule.MaxValue) return Rule.MaxValue; + if(num < Rule.MinValue) return Rule.MinValue; + else return num; + } + set + { + Registry.SetValue(Rule.RegPath, Rule.ValueName, value, Rule.ValueKind); + } + } + } + + sealed class StringRegRuleItem : RuleItem, ITsiRegPathItem + { + public struct RegRule + { + public string RegPath { get; set; } + public string ValueName { get; set; } + } + + readonly Label LblValue = new Label + { + Font = new Font(SystemFonts.MenuFont.FontFamily, 12F), + BorderStyle = BorderStyle.FixedSingle, + AutoSize = true + }; + + public RegLocationMenuItem TsiRegLocation { get; set; } + + public StringRegRuleItem(RegRule rule, ItemInfo info) : base(info) + { + this.AddCtr(LblValue); + MyToolTip.SetToolTip(LblValue, info.Tip); + TsiRegLocation = new RegLocationMenuItem(this); + this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { new ToolStripSeparator(), TsiRegLocation }); + this.Rule = rule; + LblValue.Text = ItemValue; + LblValue.MouseDown += (sender, e) => + { + using(InputDialog dlg = new InputDialog()) + { + dlg.Title = AppString.Menu.ChangeText; + dlg.Text = ItemValue; + if(dlg.ShowDialog() != DialogResult.OK) return; + ItemValue = LblValue.Text = dlg.Text; + } + }; + LblValue.TextChanged += (sender, e) => ItemValue = LblValue.Text; + } + + public string RegPath => Rule.RegPath; + public string ValueName => Rule.ValueName; + public RegRule Rule { get; set; } + + public string ItemValue + { + get => Registry.GetValue(Rule.RegPath, Rule.ValueName, null)?.ToString(); + set => Registry.SetValue(Rule.RegPath, Rule.ValueName, value); + } + } + + sealed class VisbleIniRuleItem : RuleItem, IChkVisibleItem + { + public struct IniRule + { + public string IniPath { get; set; } + public string Section { get; set; } + public string KeyName { get; set; } + public string TurnOnValue { get; set; } + public string TurnOffValue { get; set; } + } + + public VisbleIniRuleItem(IniRule rule, ItemInfo info) : base(info) + { + this.Rule = rule; + this.IniWriter = new IniWriter(rule.IniPath); + ChkVisible = new VisibleCheckBox(this) { Checked = ItemVisible }; + MyToolTip.SetToolTip(ChkVisible, info.Tip); + } + + public IniRule Rule { get; set; } + public IniWriter IniWriter { get; set; } + public VisibleCheckBox ChkVisible { get; set; } + public bool ItemVisible + { + get => IniWriter.GetValue(Rule.Section, Rule.KeyName) == Rule.TurnOnValue; + set => IniWriter.SetValue(Rule.Section, Rule.KeyName, value ? Rule.TurnOnValue : Rule.TurnOffValue); + } + } + + sealed class NumberIniRuleItem : RuleItem + { + public struct IniRule + { + public string IniPath { get; set; } + public string Section { get; set; } + public string KeyName { get; set; } + public int MaxValue { get; set; } + public int MinValue { get; set; } + public int DefaultValue { get; set; } + } + + public NumberIniRuleItem(IniRule rule, ItemInfo info) : base(info) + { + this.AddCtr(NudValue); + this.Rule = rule; + this.IniWriter = new IniWriter(rule.IniPath); + MyToolTip.SetToolTip(NudValue, info.Tip); + NudValue.Maximum = rule.MaxValue; + NudValue.Minimum = rule.MinValue; + NudValue.ValueChanged += (sender, e) => + { + if(NudValue.Value == Rule.DefaultValue) NudValue.ForeColor = Color.Red; + else NudValue.ForeColor = Color.Black; + this.ItemValue = (int)NudValue.Value; + }; + NudValue.Value = ItemValue; + } + + public IniRule Rule { get; set; } + public IniWriter IniWriter { get; set; } + + readonly NumericUpDown NudValue = new NumericUpDown + { + Font = new Font(SystemFonts.MenuFont.FontFamily, 12F), + TextAlign = HorizontalAlignment.Center, + Width = 80.DpiZoom() + }; + + public int ItemValue + { + get + { + string value = IniWriter.GetValue(Rule.Section, Rule.KeyName); + if(value.IsNullOrWhiteSpace()) return Rule.DefaultValue; + int num = Convert.ToInt32(value); + if(num > Rule.MaxValue) return Rule.MaxValue; + if(num < Rule.MinValue) return Rule.MinValue; + else return num; + } + set + { + IniWriter.SetValue(Rule.Section, Rule.KeyName, value.ToString()); + } + } + } + + sealed class StringIniRuleItem : RuleItem + { + public struct IniRule + { + public string IniPath { get; set; } + public string Secation { get; set; } + public string KeyName { get; set; } + } + + + readonly Label LblValue = new Label + { + Font = new Font(SystemFonts.MenuFont.FontFamily, 12F), + BorderStyle = BorderStyle.FixedSingle, + AutoSize = true + }; + + public StringIniRuleItem(IniRule rule, ItemInfo info) : base(info) + { + this.Rule = rule; + this.IniWriter = new IniWriter(rule.IniPath); + this.AddCtr(LblValue); + MyToolTip.SetToolTip(LblValue, info.Tip); + LblValue.Text = ItemValue; + LblValue.MouseDown += (sender, e) => + { + using(InputDialog dlg = new InputDialog()) + { + dlg.Title = AppString.Menu.ChangeText; + dlg.Text = ItemValue; + if(dlg.ShowDialog() != DialogResult.OK) return; + ItemValue = LblValue.Text = dlg.Text; + } + }; + LblValue.TextChanged += (sender, e) => ItemValue = LblValue.Text; + } + + public IniRule Rule { get; set; } + public IniWriter IniWriter { get; set; } + + public string ItemValue + { + get => IniWriter.GetValue(Rule.Secation, Rule.KeyName); + set => IniWriter.SetValue(Rule.Secation, Rule.KeyName, value); + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/SelectDialog.cs b/ContextMenuManager/Controls/SelectDialog.cs new file mode 100644 index 0000000..ea83194 --- /dev/null +++ b/ContextMenuManager/Controls/SelectDialog.cs @@ -0,0 +1,147 @@ +using BluePointLilac.Methods; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + class SelectDialog : CommonDialog + { + public string Title { get; set; } + public string Selected { get; set; } + public int SelectedIndex { get; private set; } + public string[] Items { get; set; } + public ComboBoxStyle DropDownStyle { get; set; } = ComboBoxStyle.DropDownList; + + public override void Reset() { } + + protected override bool RunDialog(IntPtr hwndOwner) + { + using(SelectForm frm = new SelectForm()) + { + frm.Text = this.Title; + frm.Items = this.Items; + frm.Selected = this.Selected; + frm.DropDownStyle = this.DropDownStyle; + bool flag = frm.ShowDialog() == DialogResult.OK; + if(flag) + { + this.Selected = frm.Selected; + this.SelectedIndex = frm.SelectedIndex; + } + return flag; + } + } + + sealed class SelectForm : Form + { + public SelectForm() + { + this.AcceptButton = btnOk; + this.CancelButton = btnCancel; + this.Font = SystemFonts.MenuFont; + this.ShowIcon = this.ShowInTaskbar = false; + this.MaximizeBox = this.MinimizeBox = false; + this.FormBorderStyle = FormBorderStyle.FixedSingle; + this.StartPosition = FormStartPosition.CenterParent; + this.InitializeComponents(); + } + + public string Selected + { + get => cmbItems.Text; + set => cmbItems.Text = value; + } + + public string[] Items + { + get + { + string[] value = new string[cmbItems.Items.Count]; + cmbItems.Items.CopyTo(value, 0); + return value; + } + set + { + cmbItems.Items.Clear(); + cmbItems.Items.AddRange(value); + } + } + + public ComboBoxStyle DropDownStyle + { + get => cmbItems.DropDownStyle; + set => cmbItems.DropDownStyle = value; + } + + public int SelectedIndex => cmbItems.SelectedIndex; + + readonly Button btnOk = new Button + { + DialogResult = DialogResult.OK, + Text = AppString.Dialog.Ok, + AutoSize = true + }; + readonly Button btnCancel = new Button + { + DialogResult = DialogResult.Cancel, + Text = AppString.Dialog.Cancel, + AutoSize = true + }; + readonly ComboBox cmbItems = new ComboBox + { + AutoCompleteMode = AutoCompleteMode.SuggestAppend, + AutoCompleteSource = AutoCompleteSource.ListItems, + DropDownHeight = 294.DpiZoom(), + ImeMode = ImeMode.Disable + }; + + private void InitializeComponents() + { + this.Controls.AddRange(new Control[] { cmbItems, btnOk, btnCancel }); + int a = 20.DpiZoom(); + cmbItems.Left = a; + cmbItems.Width = 85.DpiZoom(); + cmbItems.Top = btnOk.Top = btnCancel.Top = a; + btnOk.Left = cmbItems.Right + a; + btnCancel.Left = btnOk.Right + a; + this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a); + } + } + } + + sealed class FileExtensionDialog : SelectDialog + { + public string Extension + { + get => Selected; + set => Selected = value; + } + + public FileExtensionDialog() + { + this.Title = AppString.Item.SelectExtension; + this.DropDownStyle = ComboBoxStyle.DropDown; + List items = new List(); + foreach(string keyName in Microsoft.Win32.Registry.ClassesRoot.GetSubKeyNames()) + { + if(keyName.StartsWith(".")) items.Add(keyName.Substring(1)); + } + this.Items = items.ToArray(); + } + + protected override bool RunDialog(IntPtr hwndOwner) + { + bool flag = base.RunDialog(hwndOwner); + if(flag) + { + string extension = ObjectPath.RemoveIllegalChars(this.Extension); + int index = extension.LastIndexOf('.'); + if(index >= 0) this.Extension = extension.Substring(index); + else this.Extension = $".{extension}"; + } + return flag; + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/SendToItem.cs b/ContextMenuManager/Controls/SendToItem.cs index c7589cc..bc76d0c 100644 --- a/ContextMenuManager/Controls/SendToItem.cs +++ b/ContextMenuManager/Controls/SendToItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System.Drawing; @@ -8,8 +8,8 @@ namespace ContextMenuManager.Controls { - sealed class SendToItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem, - ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem + sealed class SendToItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem, ITsiAdministratorItem, + ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, ITsiShortcutCommandItem { public SendToItem(string filePath) { @@ -24,15 +24,14 @@ public string FilePath set { filePath = value; - if(IsShortcut) this.Shortcut.FullName = value; + if(IsShortcut) this.Shortcut = new WshShortcut(value); this.Text = this.ItemText; this.Image = this.ItemIcon.ToBitmap(); ChkVisible.Checked = this.ItemVisible; } } - private WshShortcut Shortcut = new WshShortcut(); - private string FileName => Path.GetFileName(FilePath); + public WshShortcut Shortcut { get; private set; } private string FileExtension => Path.GetExtension(FilePath); private bool IsShortcut => FileExtension.ToLower() == ".lnk"; public string SearchText => $"{AppString.SideBar.SendTo} {Text}"; @@ -41,13 +40,25 @@ public string ItemFilePath { get { - if(IsShortcut) return Shortcut.TargetPath; + string path = null; + if(IsShortcut) path = Shortcut.TargetPath; else { - string guidPath = Registry.ClassesRoot.OpenSubKey(FileExtension)?.GetValue("")?.ToString(); - if(string.IsNullOrEmpty(guidPath)) return null; - else return Registry.ClassesRoot.OpenSubKey($@"{guidPath}\InProcServer32")?.GetValue("")?.ToString(); + using(RegistryKey root = Registry.ClassesRoot) + using(RegistryKey extKey = root.OpenSubKey(FileExtension)) + { + string guidPath = extKey?.GetValue("")?.ToString(); + if(!string.IsNullOrEmpty(guidPath)) + { + using(RegistryKey ipsKey = root.OpenSubKey($@"{guidPath}\InProcServer32")) + { + path = ipsKey?.GetValue("")?.ToString(); + } + } + } } + if(!File.Exists(path) && !Directory.Exists(path)) path = FilePath; + return path; } } @@ -67,17 +78,16 @@ public string ItemText { get { - string name = SendToList.DesktopIniReader.GetValue("LocalizedFileNames", FileName); - name = ResourceString.GetDirectString(name); + string name = DesktopIni.GetLocalizedFileNames(FilePath, true); if(name == string.Empty) name = Path.GetFileNameWithoutExtension(FilePath); if(name == string.Empty) name = FileExtension; return name; } set { - SendToList.DesktopIniWriter.SetValue("LocalizedFileNames", FileName, value); + DesktopIni.SetLocalizedFileNames(FilePath, value); this.Text = ResourceString.GetDirectString(value); - ExplorerRestarter.NeedRestart = true; + ExplorerRestarter.Show(); } } @@ -87,12 +97,14 @@ public Icon ItemIcon { Icon icon = ResourceIcon.GetIcon(IconLocation, out string iconPath, out int iconIndex); IconPath = iconPath; IconIndex = iconIndex; - if(icon == null && IsShortcut) + if(icon != null) return icon; + if(IsShortcut) { - if(File.Exists(Shortcut.TargetPath)) icon = Icon.ExtractAssociatedIcon(Shortcut.TargetPath); - else if(Directory.Exists(Shortcut.TargetPath)) icon = ResourceIcon.GetFolderIcon(Shortcut.TargetPath); + string path = ItemFilePath; + if(File.Exists(path)) icon = ResourceIcon.GetExtensionIcon(path); + else if(Directory.Exists(path)) icon = ResourceIcon.GetFolderIcon(path); } - icon = icon ?? ResourceIcon.GetExtensionIcon(FileExtension); + else icon = ResourceIcon.GetExtensionIcon(FileExtension); return icon; } } @@ -105,7 +117,7 @@ public string IconLocation if(IsShortcut) { location = Shortcut.IconLocation; - if(location.StartsWith(",")) location = $"{Shortcut.TargetPath}{location}"; + if(location==",0") location = Shortcut.TargetPath; } else { @@ -128,7 +140,7 @@ public string IconLocation { if(IsShortcut) { - Shortcut.IconLocation = value; + Shortcut.IconLocation = $"{this.IconPath},{this.IconIndex}"; Shortcut.Save(); } else @@ -142,7 +154,7 @@ public string IconLocation string regPath = $@"{root.Name}\{guidPath}\DefaultIcon"; RegTrustedInstaller.TakeRegTreeOwnerShip(regPath); Registry.SetValue(regPath, "", value); - ExplorerRestarter.NeedRestart = true; + ExplorerRestarter.Show(); } } } @@ -160,8 +172,10 @@ public string IconLocation public FilePropertiesMenuItem TsiFileProperties { get; set; } public FileLocationMenuItem TsiFileLocation { get; set; } public DeleteMeMenuItem TsiDeleteMe { get; set; } + public ShortcutCommandMenuItem TsiChangeCommand { get; set; } + public RunAsAdministratorItem TsiAdministrator { get; set; } + readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details); - readonly ToolStripMenuItem TsiChangeCommand = new ToolStripMenuItem(AppString.Menu.ChangeCommand); private void InitializeComponents() { @@ -169,40 +183,36 @@ private void InitializeComponents() ChkVisible = new VisibleCheckBox(this); TsiChangeText = new ChangeTextMenuItem(this); TsiChangeIcon = new ChangeIconMenuItem(this); + TsiChangeCommand = new ShortcutCommandMenuItem(this); + TsiAdministrator = new RunAsAdministratorItem(this); TsiSearch = new WebSearchMenuItem(this); TsiFileLocation = new FileLocationMenuItem(this); TsiFileProperties = new FilePropertiesMenuItem(this); TsiDeleteMe = new DeleteMeMenuItem(this); ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, new ToolStripSeparator(), - TsiChangeIcon, new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); + TsiChangeIcon, new ToolStripSeparator(), TsiAdministrator, new ToolStripSeparator(), + TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(), TsiChangeCommand, TsiFileProperties, TsiFileLocation }); ContextMenuStrip.Opening += (sender, e) => TsiChangeCommand.Visible = IsShortcut; - TsiChangeCommand.Click += (sender, e) => ChangeCommand(); - - } - - private void ChangeCommand() - { - using(CommandDialog dlg = new CommandDialog()) + TsiChangeCommand.Click += (sender, e) => { - dlg.Command = Shortcut.TargetPath; - dlg.Arguments = Shortcut.Arguments; - if(dlg.ShowDialog() != DialogResult.OK) return; - Shortcut.TargetPath = dlg.Command; - Shortcut.Arguments = dlg.Arguments; - Shortcut.Save(); - } + if(TsiChangeCommand.ChangeCommand(Shortcut)) + { + Image = ItemIcon.ToBitmap(); + } + }; } public void DeleteMe() { File.Delete(this.FilePath); - SendToList.DesktopIniWriter.DeleteKey("LocalizedFileNames", FileName); + DesktopIni.DeleteLocalizedFileNames(FilePath); + this.Shortcut.Dispose(); this.Dispose(); } } diff --git a/ContextMenuManager/Controls/SendToList.cs b/ContextMenuManager/Controls/SendToList.cs index a56aa26..9b2ab69 100644 --- a/ContextMenuManager/Controls/SendToList.cs +++ b/ContextMenuManager/Controls/SendToList.cs @@ -1,7 +1,6 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; -using System.Diagnostics; using System.IO; using System.Windows.Forms; @@ -10,23 +9,19 @@ namespace ContextMenuManager.Controls sealed class SendToList : MyList { public static readonly string SendToPath = Environment.ExpandEnvironmentVariables(@"%AppData%\Microsoft\Windows\SendTo"); - public static readonly string DesktopIniPath = $@"{SendToPath}\desktop.ini"; - public static IniWriter DesktopIniWriter = new IniWriter(DesktopIniPath); - public static IniReader DesktopIniReader; public void LoadItems() { - DesktopIniReader = new IniReader(DesktopIniPath); - Array.ForEach(new DirectoryInfo(SendToPath).GetFiles(), fi => + Array.ForEach(Directory.GetFiles(SendToPath), path => { - if(fi.Name.ToLower() != "desktop.ini") - this.AddItem(new SendToItem(fi.FullName)); + if(Path.GetFileName(path).ToLower() != "desktop.ini") + this.AddItem(new SendToItem(path)); }); this.SortItemByText(); this.AddNewItem(); this.AddDirItem(); - this.AddItem(new RegRuleItem(RegRuleItem.SendToDrive)); - this.AddItem(new RegRuleItem(RegRuleItem.DeferBuildSendTo)); + this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.SendToDrive)); + this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.DeferBuildSendTo)); } private void AddNewItem() @@ -35,10 +30,21 @@ private void AddNewItem() this.InsertItem(newItem, 0); newItem.AddNewItem += (sender, e) => { - using(NewSendToDialog dlg = new NewSendToDialog()) + using(NewLnkFileDialog dlg = new NewLnkFileDialog()) { - if(dlg.ShowDialog() == DialogResult.OK) - this.InsertItem(new SendToItem(dlg.FilePath), 2); + dlg.FileFilter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.vbs;*.vbe;*.js;*.jse;*.wsf"; + if(dlg.ShowDialog() != DialogResult.OK) return; + string lnkPath = $@"{SendToPath}\{ObjectPath.RemoveIllegalChars(dlg.ItemText)}.lnk"; + lnkPath = ObjectPath.GetNewPathWithIndex(lnkPath, ObjectPath.PathType.File); + using(WshShortcut shortcut = new WshShortcut(lnkPath)) + { + shortcut.TargetPath = dlg.ItemFilePath; + shortcut.WorkingDirectory = Path.GetDirectoryName(dlg.ItemFilePath); + shortcut.Arguments = dlg.Arguments; + shortcut.Save(); + } + DesktopIni.SetLocalizedFileNames(lnkPath, dlg.ItemText); + this.InsertItem(new SendToItem(lnkPath), 2); } }; } @@ -52,7 +58,7 @@ private void AddDirItem() }; PictureButton btnPath = new PictureButton(AppImage.Open); MyToolTip.SetToolTip(btnPath, AppString.Menu.FileLocation); - btnPath.MouseDown += (sender, e) => Process.Start(SendToPath); + btnPath.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(SendToPath); item.AddCtr(btnPath); item.SetNoClickEvent(); this.InsertItem(item, 1); diff --git a/ContextMenuManager/Controls/ShellExItem.cs b/ContextMenuManager/Controls/ShellExItem.cs index 58cadf7..f037882 100644 --- a/ContextMenuManager/Controls/ShellExItem.cs +++ b/ContextMenuManager/Controls/ShellExItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; @@ -8,15 +8,16 @@ namespace ContextMenuManager.Controls { - sealed class ShellExItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, + sealed class ShellExItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, IFoldSubItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiRegDeleteItem, ITsiRegExportItem { - public static Dictionary GetPathAndGuids(string shellExPath) + public static Dictionary GetPathAndGuids(string shellExPath, bool isDragDrop = false) { Dictionary dic = new Dictionary(); - foreach(string cmhPart in CmhParts) + string[] parts = isDragDrop ? DdhParts : CmhParts; + foreach(string part in parts) { - using(RegistryKey cmKey = RegistryEx.GetRegistryKey($@"{shellExPath}\{cmhPart}")) + using(RegistryKey cmKey = RegistryEx.GetRegistryKey($@"{shellExPath}\{part}")) { if(cmKey == null) continue; foreach(string keyName in cmKey.GetSubKeyNames()) @@ -34,6 +35,7 @@ sealed class ShellExItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, return dic; } + public static readonly string[] DdhParts = { "DragDropHandlers", "-DragDropHandlers" }; public static readonly string[] CmhParts = { "ContextMenuHandlers", "-ContextMenuHandlers" }; private const string LnkOpenGuid = "00021401-0000-0000-c000-000000000046"; @@ -57,26 +59,39 @@ public string RegPath } } + public string ValueName => DefaultValue; public Guid Guid { get; set; } public string SearchText => Text; public string ItemFilePath => GuidInfo.GetFilePath(Guid); private string KeyName => RegistryEx.GetKeyName(RegPath); - private string ShellExPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegPath)); - private string CmhKeyName => RegistryEx.GetKeyName(RegistryEx.GetParentPath(RegPath)); + private string ParentPath => RegistryEx.GetParentPath(RegPath); + private string ShellExPath => RegistryEx.GetParentPath(ParentPath); + private string ParentKeyName => RegistryEx.GetKeyName(ParentPath); private string DefaultValue => Registry.GetValue(RegPath, "", null)?.ToString(); public string ItemText => GuidInfo.GetText(Guid) ?? ((Guid.ToString("B") == KeyName) ? DefaultValue : KeyName); - private string BackupPath => $@"{ShellExPath}\{(ItemVisible ? CmhParts[1] : CmhParts[0])}\{KeyName}"; private GuidInfo.IconLocation IconLocation => GuidInfo.GetIconLocation(this.Guid); private bool IsOpenLnkItem => Guid.ToString() == LnkOpenGuid; - private bool TryProtectOpenItem => IsOpenLnkItem && AppConfig.ProtectOpenItem && MessageBoxEx.Show - (AppString.MessageBox.PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes; + public bool IsDragDropItem => ParentKeyName.EndsWith(DdhParts[0], StringComparison.OrdinalIgnoreCase); + + private string BackupPath + { + get + { + string[] parts = IsDragDropItem ? DdhParts : CmhParts; + return $@"{ShellExPath}\{(ItemVisible ? parts[1] : parts[0])}\{KeyName}"; + } + } public bool ItemVisible { - get => CmhKeyName.Equals(CmhParts[0], StringComparison.OrdinalIgnoreCase); + get + { + string[] parts = IsDragDropItem ? DdhParts : CmhParts; + return ParentKeyName.Equals(parts[0], StringComparison.OrdinalIgnoreCase); + } set { - if(!value && TryProtectOpenItem) return; + if(!value && TryProtectOpenItem()) return; try { RegistryEx.MoveTo(RegPath, BackupPath); @@ -98,6 +113,7 @@ public bool ItemVisible public RegLocationMenuItem TsiRegLocation { get; set; } public DeleteMeMenuItem TsiDeleteMe { get; set; } public RegExportMenuItem TsiRegExport { get; set; } + public IFoldGroupItem FoldGroupItem { get; set; } readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details); readonly ToolStripMenuItem TsiHandleGuid = new ToolStripMenuItem(AppString.Menu.HandleGuid); @@ -151,7 +167,7 @@ private void BlockGuid() Registry.SetValue(path, this.Guid.ToString("B"), string.Empty); } } - ExplorerRestarter.NeedRestart = true; + ExplorerRestarter.Show(); } private void AddGuidDic() @@ -240,6 +256,13 @@ private void RefreshMenuItem() } } + private bool TryProtectOpenItem() + { + if(!IsOpenLnkItem) return false; + if(!AppConfig.ProtectOpenItem) return false; + return MessageBoxEx.Show(AppString.MessageBox.PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes; + } + public void DeleteMe() { try diff --git a/ContextMenuManager/Controls/ShellExecuteDialog.cs b/ContextMenuManager/Controls/ShellExecuteDialog.cs new file mode 100644 index 0000000..aaf6525 --- /dev/null +++ b/ContextMenuManager/Controls/ShellExecuteDialog.cs @@ -0,0 +1,162 @@ +using BluePointLilac.Methods; +using System; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + sealed class ShellExecuteDialog : CommonDialog + { + public string Verb { get; set; } + public int WindowStyle { get; set; } + public override void Reset() { } + + protected override bool RunDialog(IntPtr hwndOwner) + { + using(ShellExecuteForm frm = new ShellExecuteForm()) + { + bool flag = frm.ShowDialog() == DialogResult.OK; + if(flag) + { + this.Verb = frm.Verb; + this.WindowStyle = frm.WindowStyle; + } + return flag; + } + } + + public static string GetCommand(string fileName, string arguments, string verb, int windowStyle, string directory = null) + { + arguments = arguments.Replace("\"", "\"\""); + if(directory == null) + { + ObjectPath.GetFullFilePath(fileName, out string filePath); + directory = Path.GetDirectoryName(filePath); + } + return "mshta vbscript:createobject(\"shell.application\").shellexecute" + + $"(\"{fileName}\",\"{arguments}\",\"{directory}\",\"{verb}\",{windowStyle})(close)"; + } + + sealed class ShellExecuteForm : Form + { + private const string ApiInfoUrl = "https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea"; + private static readonly string[] Verbs = new[] { "open", "runas", "edit", "print", "find", "explore" }; + public ShellExecuteForm() + { + this.Text = "ShellExecute"; + this.Font = SystemFonts.MenuFont; + this.FormBorderStyle = FormBorderStyle.FixedSingle; + this.StartPosition = FormStartPosition.CenterParent; + this.ShowIcon = ShowInTaskbar = MaximizeBox = MinimizeBox = false; + this.HelpButton = true; + this.HelpButtonClicked += (sender, e) => ExternalProgram.OpenUrl(ApiInfoUrl); + this.InitializeComponents(); + } + public string Verb { get; set; } + public int WindowStyle { get; set; } + + readonly RadioButton[] rdoVerbs = new RadioButton[6]; + readonly GroupBox grpVerb = new GroupBox { Text = "Verb" }; + readonly Label lblStyle = new Label + { + Text = "WindowStyle", + AutoSize = true + }; + readonly NumericUpDown nudStyle = new NumericUpDown + { + TextAlign = HorizontalAlignment.Center, + Width = 80.DpiZoom(), + Maximum = 10, + Minimum = 0, + Value = 1 + }; + readonly Button btnOk = new Button + { + Text = AppString.Dialog.Ok, + DialogResult = DialogResult.OK, + AutoSize = true + }; + readonly Button btnCancel = new Button + { + Text = AppString.Dialog.Cancel, + DialogResult = DialogResult.Cancel, + AutoSize = true + }; + + private void InitializeComponents() + { + this.Controls.AddRange(new Control[] { grpVerb, lblStyle, nudStyle, btnOk, btnCancel }); + int a = 10.DpiZoom(); + int b = 2 * a; + for(int i = 0; i < 6; i++) + { + rdoVerbs[i] = new RadioButton + { + Text = Verbs[i], + AutoSize = true, + Parent = grpVerb, + Location = new Point(a, b + a) + }; + if(i > 0) rdoVerbs[i].Left += rdoVerbs[i - 1].Right; + } + rdoVerbs[0].Checked = true; + grpVerb.Width = rdoVerbs[5].Right + a; + grpVerb.Height = rdoVerbs[5].Bottom + b; + lblStyle.Left = grpVerb.Left = grpVerb.Top = b; + btnOk.Top = btnCancel.Top = lblStyle.Top = nudStyle.Top = grpVerb.Bottom + b; + nudStyle.Left = lblStyle.Right + b; + btnCancel.Left = grpVerb.Right - btnCancel.Width; + btnOk.Left = btnCancel.Left - btnOk.Width - b; + this.ClientSize = new Size(btnCancel.Right + b, btnCancel.Bottom + b); + btnOk.Click += (sender, e) => + { + for(int i = 0; i < 6; i++) + { + if(rdoVerbs[i].Checked) + { + this.Verb = rdoVerbs[i].Text; + break; + } + } + this.WindowStyle = (int)nudStyle.Value; + }; + } + } + } + + sealed class ShellExecuteCheckBox : CheckBox + { + public ShellExecuteCheckBox() + { + this.Text = "ShellExecute"; + this.AutoSize = true; + this.Font = new Font(SystemFonts.DialogFont.FontFamily, 8F); + } + + public string Verb { get; set; } + public int WindowStyle { get; set; } + + readonly ToolTip ttpInfo = new ToolTip(); + + protected override void OnClick(EventArgs e) + { + if(this.Checked) + { + this.Checked = false; + ttpInfo.RemoveAll(); + } + else + { + using(ShellExecuteDialog dlg = new ShellExecuteDialog()) + { + if(dlg.ShowDialog() != DialogResult.OK) return; + this.Verb = dlg.Verb; + this.WindowStyle = dlg.WindowStyle; + this.Checked = true; + ttpInfo.SetToolTip(this, $"Verb = {Verb}\nWindowStyle = {WindowStyle}"); + } + } + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/ShellItem.cs b/ContextMenuManager/Controls/ShellItem.cs index c2bcca7..58ca6a3 100644 --- a/ContextMenuManager/Controls/ShellItem.cs +++ b/ContextMenuManager/Controls/ShellItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; @@ -16,6 +16,8 @@ class ShellItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem, I /// Shell公共引用子菜单注册表项路径 public const string CommandStorePath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell"; + private const string OpenInNewWindowPath = @"HKEY_CLASSES_ROOT\Folder\shell\opennewwindow"; + /// 系统原有Shell公共子菜单项名 public static readonly string[] SysStoreItemNames = { "Windows.aboutWindows", "Windows.AddColumns", "Windows.AddDevice", "Windows.AddMediaServer", "Windows.AddNetworkLocation", "Windows.AddPrinter", @@ -119,18 +121,28 @@ public string RegPath } } + public string ValueName => null; public string SearchText => Text; private string CommandPath => $@"{RegPath}\command"; public string KeyName => RegistryEx.GetKeyName(RegPath); - private bool IsMultiItem => Registry.GetValue(RegPath, "SubCommands", null) != null; protected virtual bool IsSubItem => false; private bool IsOpenItem => KeyName.ToLower() == "open"; - private bool TryProtectOpenItem => IsOpenItem && AppConfig.ProtectOpenItem && MessageBoxEx.Show(AppString.MessageBox.PromptIsOpenItem, - MessageBoxButtons.YesNo) != DialogResult.Yes; public string ItemFilePath => GuidInfo.GetFilePath(Guid) ?? ObjectPath.ExtractFilePath(ItemCommand); private bool HasIcon => !IconLocation.IsNullOrWhiteSpace() || HasLUAShield; + private bool IsMultiItem + { + get + { + object value = Registry.GetValue(RegPath, "SubCommands", null); + if(value != null) return true; + value = Registry.GetValue(RegPath, "ExtendedSubCommandsKey", null); + if(!string.IsNullOrEmpty(value?.ToString())) return true; + return false; + } + } + private bool OnlyInExplorer { get => Registry.GetValue(RegPath, "OnlyInBrowserWindow", null) != null; @@ -138,7 +150,7 @@ private bool OnlyInExplorer { if(value) { - if(TryProtectOpenItem) return; + if(TryProtectOpenItem()) return; Registry.SetValue(RegPath, "OnlyInBrowserWindow", ""); } else RegistryEx.DeleteValue(RegPath, "OnlyInBrowserWindow"); @@ -152,7 +164,7 @@ private bool OnlyWithShift { if(value) { - if(TryProtectOpenItem) return; + if(TryProtectOpenItem()) return; Registry.SetValue(RegPath, "Extended", ""); } else RegistryEx.DeleteValue(RegPath, "Extended"); @@ -179,6 +191,16 @@ private bool NeverDefault } } + private bool ShowAsDisabledIfHidden + { + get => Registry.GetValue(RegPath, "ShowAsDisabledIfHidden", null) != null; + set + { + if(value) Registry.SetValue(RegPath, "ShowAsDisabledIfHidden", ""); + else RegistryEx.DeleteValue(RegPath, "ShowAsDisabledIfHidden"); + } + } + private Positions ItemPosition { get @@ -234,24 +256,38 @@ public bool ItemVisible { try { - if(value) + void DeleteSomeValues() { - RegistryEx.DeleteValue(RegPath, "CommandFlags"); - RegistryEx.DeleteValue(RegPath, "HideBasedOnVelocityId"); RegistryEx.DeleteValue(RegPath, "LegacyDisable"); RegistryEx.DeleteValue(RegPath, "ProgrammaticAccessOnly"); + if(WindowsOsVersion.IsAfterVista && Convert.ToInt32(Registry.GetValue(RegPath, "CommandFlags", 0)) % 16 >= 8) + { + RegistryEx.DeleteValue(RegPath, "CommandFlags"); + } + }; + + if(value) + { + RegistryEx.DeleteValue(RegPath, "HideBasedOnVelocityId"); + DeleteSomeValues(); } else { - if(TryProtectOpenItem) return; + if(TryProtectOpenItem()) return; if(!IsSubItem) { - Registry.SetValue(RegPath, "LegacyDisable", string.Empty); - Registry.SetValue(RegPath, "ProgrammaticAccessOnly", string.Empty); + //当LegaryDisable键值作用于文件夹-"在新窗口中打开"时 + //会导致点击任务栏explorer图标和 Win+E 快捷键错误访问 + if(!RegPath.StartsWith(OpenInNewWindowPath, StringComparison.OrdinalIgnoreCase)) + { + Registry.SetValue(RegPath, "LegacyDisable", ""); + } + Registry.SetValue(RegPath, "ProgrammaticAccessOnly", ""); } - else if(WindowsOsVersion.IsAfterOrEqualWin10_1703) + if(WindowsOsVersion.IsAfterOrEqualWin10_1703) { Registry.SetValue(RegPath, "HideBasedOnVelocityId", 0x639bc8); + if(ShowAsDisabledIfHidden) DeleteSomeValues(); } else { @@ -307,7 +343,7 @@ public string ItemCommand } set { - if(TryProtectOpenItem) return; + if(TryProtectOpenItem()) return; Registry.SetValue(CommandPath, "", value); if(!this.HasIcon) this.Image = this.ItemIcon.ToBitmap().ToTransparent(); } @@ -365,7 +401,11 @@ private Guid Guid get { string value = Registry.GetValue(CommandPath, "DelegateExecute", null)?.ToString(); - GuidEx.TryParse(value, out Guid guid); + if(!GuidEx.TryParse(value, out Guid guid)) + { + value = Registry.GetValue($@"{RegPath}\DropTarget", "CLSID", null)?.ToString(); + GuidEx.TryParse(value, out guid); + } return guid; } } @@ -382,6 +422,7 @@ private Guid Guid public DeleteMeMenuItem TsiDeleteMe { get; set; } public RegExportMenuItem TsiRegExport { get; set; } + protected readonly PictureButton BtnSubItems = new PictureButton(AppImage.SubItems); protected readonly ToolStripMenuItem TsiOtherAttributes = new ToolStripMenuItem(AppString.Menu.OtherAttributes); readonly ToolStripMenuItem TsiItemIcon = new ToolStripMenuItem(AppString.Menu.ItemIcon); readonly ToolStripMenuItem TsiDeleteIcon = new ToolStripMenuItem(AppString.Menu.DeleteIcon); @@ -395,7 +436,7 @@ private Guid Guid readonly ToolStripMenuItem TsiNoWorkDir = new ToolStripMenuItem(AppString.Menu.NoWorkingDirectory); readonly ToolStripMenuItem TsiNeverDefault = new ToolStripMenuItem(AppString.Menu.NeverDefault); readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details); - protected readonly PictureButton BtnSubItems = new PictureButton(AppImage.SubItems); + readonly ToolStripMenuItem TsiShowAsDisabled = new ToolStripMenuItem(AppString.Menu.ShowAsDisabledIfHidden); private void InitializeComponents() { @@ -418,7 +459,8 @@ private void InitializeComponents() TsiPosition.DropDownItems.AddRange(new ToolStripItem[] { TsiDefault, TsiSetTop, TsiSetBottom }); - TsiOtherAttributes.DropDownItems.AddRange(new ToolStripItem[] { TsiOnlyWithShift, TsiOnlyInExplorer, TsiNoWorkDir, TsiNeverDefault }); + TsiOtherAttributes.DropDownItems.AddRange(new ToolStripItem[] { TsiOnlyWithShift, TsiOnlyInExplorer, + TsiNoWorkDir, TsiNeverDefault, TsiShowAsDisabled }); TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(), TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport}); @@ -431,6 +473,7 @@ private void InitializeComponents() TsiOnlyWithShift.Click += (sender, e) => this.OnlyWithShift = !TsiOnlyWithShift.Checked; TsiNoWorkDir.Click += (sender, e) => this.NoWorkingDirectory = !TsiNoWorkDir.Checked; TsiNeverDefault.Click += (sender, e) => this.NeverDefault = !TsiNeverDefault.Checked; + TsiShowAsDisabled.Click += (sender, e) => SetDisabled(!TsiShowAsDisabled.Checked); ContextMenuStrip.Opening += (sender, e) => RefreshMenuItem(); BtnSubItems.MouseDown += (sender, e) => ShowSubItems(); TsiShieldIcon.Click += (sender, e) => UseShieldIcon(); @@ -448,19 +491,34 @@ private void DeleteIcon() private void UseShieldIcon() { bool flag = this.HasLUAShield = TsiShieldIcon.Checked = !TsiShieldIcon.Checked; - if(flag && IconLocation == null) + if(IconLocation == null) { - this.Image = AppImage.Shield; - this.IconPath = "imageres.dll"; - this.IconIndex = -78; + if(flag) + { + this.Image = AppImage.Shield; + this.IconPath = "imageres.dll"; + this.IconIndex = -78; + } + else + { + this.Image = this.Image.ToTransparent(); + } } } + private void SetDisabled(bool flag) + { + this.ShowAsDisabledIfHidden = flag; + if(!ItemVisible && flag) ItemVisible = false; + } + private void RefreshMenuItem() { TsiOnlyWithShift.Visible = !IsSubItem; TsiDeleteMe.Enabled = !(IsOpenItem && AppConfig.ProtectOpenItem); TsiNoWorkDir.Checked = this.NoWorkingDirectory; + TsiShowAsDisabled.Visible = WindowsOsVersion.IsAfterOrEqualWin10_1703; + TsiShowAsDisabled.Checked = this.ShowAsDisabledIfHidden; TsiChangeCommand.Visible = !IsMultiItem && Guid.Equals(Guid.Empty); if(!this.IsSubItem) TsiOnlyWithShift.Checked = this.OnlyWithShift; @@ -525,6 +583,13 @@ private void ShowSubItems() } } + private bool TryProtectOpenItem() + { + if(!IsOpenItem) return false; + if(!AppConfig.ProtectOpenItem) return false; + return MessageBoxEx.Show(AppString.MessageBox.PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes; + } + public virtual void DeleteMe() { try diff --git a/ContextMenuManager/Controls/ShellList.cs b/ContextMenuManager/Controls/ShellList.cs index 285b957..910f5fc 100644 --- a/ContextMenuManager/Controls/ShellList.cs +++ b/ContextMenuManager/Controls/ShellList.cs @@ -1,11 +1,11 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; -using System.Threading; using System.Windows.Forms; namespace ContextMenuManager.Controls @@ -24,49 +24,76 @@ sealed class ShellList : MyList public const string MENUPATH_LIBRARY = @"HKEY_CLASSES_ROOT\LibraryFolder";//库 public const string MENUPATH_LIBRARY_BACKGROUND = @"HKEY_CLASSES_ROOT\LibraryFolder\Background";//库背景 public const string MENUPATH_LIBRARY_USER = @"HKEY_CLASSES_ROOT\UserLibraryFolder";//用户库 - public const string MENUPATH_LNKFILE = @"HKEY_CLASSES_ROOT\lnkfile";//快捷方式 - public const string MENUPATH_EXEFILE = @"HKEY_CLASSES_ROOT\exefile";//可执行文件 - public const string MENUPATH_SYSLNKFILE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\.lnk";//快捷方式 - public const string MENUPATH_SYSEXEFILE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\.exe";//可执行文件 public const string MENUPATH_UWPLNK = @"HKEY_CLASSES_ROOT\Launcher.ImmersiveApplication";//UWP快捷方式 public const string MENUPATH_UNKNOWN = @"HKEY_CLASSES_ROOT\Unknown";//未知格式 - public const string MENUPATH_TEXT = @"HKEY_CLASSES_ROOT\SystemFileAssociations\text";//通用文本文件 - public const string MENUPATH_DOCUMENT = @"HKEY_CLASSES_ROOT\SystemFileAssociations\document";//通用文档文件 - public const string MENUPATH_IMAGE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\image";//通用图像文件 - public const string MENUPATH_VIDEO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\video";//通用视频文件 - public const string MENUPATH_AUDIO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\audio";//通用音频文件 - public const string MENUPATH_DIRECTORY_IMAGE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\Directory.Image";//通用图像文件目录 - public const string MENUPATH_DIRECTORY_VIDEO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\Directory.Video";//通用视频文件目录 - public const string MENUPATH_DIRECTORY_AUDIO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\Directory.Audio";//通用音频文件目录 public const string SYSFILEASSPATH = @"HKEY_CLASSES_ROOT\SystemFileAssociations";//系统扩展名注册表父项路径 public enum Scenes { - File, Folder, Directory, Background, Desktop, Drive, AllObjects, Computer, RecycleBin, - Library, LnkFile, UwpLnk, ExeFile, TextFile, DocumentFile, ImageFile, VideoFile, AudioFile, - ImageDirectory, VideoDirectory, AudioDirectory, UnknownType, CustomType, CommandStore + File, Folder, Directory, Background, Desktop, Drive, AllObjects, Computer, RecycleBin, Library, + LnkFile, UwpLnk, ExeFile, UnknownType, CustomExtension, PerceivedType, DirectoryType, CommandStore, DragDrop } - private Scenes scene; - public Scenes Scene + private static readonly string[] DirectoryTypes = { "Document", "Image", "Video", "Audio" }; + private static readonly string[] PerceivedTypes = { "Text", "Document", "Image", "Video", "Audio", "Compressed", "System" }; + private static readonly string[] DirectoryTypeNames = { - get => scene; - set { scene = value; LoadItems(); } + AppString.Dialog.DocumentDirectory, AppString.Dialog.ImageDirectory, + AppString.Dialog.VideoDirectory, AppString.Dialog.AudioDirectory + }; + private static readonly string[] PerceivedTypeNames = + { + AppString.Dialog.TextFile, AppString.Dialog.DocumentFile, AppString.Dialog.ImageFile, AppString.Dialog.VideoFile, + AppString.Dialog.AudioFile, AppString.Dialog.CompressedFile, AppString.Dialog.SystemFile + }; + + private static string GetDirectoryTypeName() + { + if(CurrentDirectoryType != null) + { + for(int i = 0; i < DirectoryTypes.Length; i++) + { + if(CurrentDirectoryType.Equals(DirectoryTypes[i], StringComparison.OrdinalIgnoreCase)) + { + return DirectoryTypeNames[i]; + } + } + } + return null; } + private static string GetPerceivedTypeName() + { + if(CurrentPerceivedType != null) + { + for(int i = 0; i < PerceivedTypes.Length; i++) + { + if(CurrentPerceivedType.Equals(PerceivedTypes[i], StringComparison.OrdinalIgnoreCase)) + { + return PerceivedTypeNames[i]; + } + } + } + return null; + } + + private static string CurrentExtension = null; + private static string CurrentDirectoryType = null; + private static string CurrentPerceivedType = null; + private static string GetShellPath(string scenePath) => $@"{scenePath}\shell"; private static string GetShellExPath(string scenePath) => $@"{scenePath}\shellEx"; + private static string GetSysAssExtPath(string typeName) => typeName != null ? $@"{SYSFILEASSPATH}\{typeName}" : null; + private static string GetOpenModePath(string extension) => extension != null ? $@"HKEY_CLASSES_ROOT\{FileExtension.GetOpenMode(extension)}" : null; + + public Scenes Scene { get; set; } public ShellList() { - TypeItem.ExtensionChanged += (sender, e) => - { - this.ClearItems(); - this.Scene = Scenes.CustomType; - }; + SelectItem.SelectedChanged += (sender, e) => { this.ClearItems(); this.LoadItems(); }; } - private void LoadItems() + public void LoadItems() { string scenePath = null; switch(Scene) @@ -96,79 +123,82 @@ private void LoadItems() if(WindowsOsVersion.IsEqualVista) return; scenePath = MENUPATH_LIBRARY; break; case Scenes.LnkFile: - scenePath = MENUPATH_LNKFILE; break; + scenePath = GetSysAssExtPath(".lnk"); break; case Scenes.UwpLnk: //Win8之前没有Uwp if(WindowsOsVersion.IsBefore8) return; scenePath = MENUPATH_UWPLNK; break; case Scenes.ExeFile: - scenePath = MENUPATH_EXEFILE; break; - case Scenes.TextFile: - scenePath = MENUPATH_TEXT; break; - case Scenes.DocumentFile: - scenePath = MENUPATH_DOCUMENT; break; - case Scenes.ImageFile: - scenePath = MENUPATH_IMAGE; break; - case Scenes.VideoFile: - scenePath = MENUPATH_VIDEO; break; - case Scenes.AudioFile: - scenePath = MENUPATH_AUDIO; break; - case Scenes.ImageDirectory: - scenePath = MENUPATH_DIRECTORY_IMAGE; break; - case Scenes.VideoDirectory: - scenePath = MENUPATH_DIRECTORY_VIDEO; break; - case Scenes.AudioDirectory: - scenePath = MENUPATH_DIRECTORY_AUDIO; break; + scenePath = GetSysAssExtPath(".exe"); break; case Scenes.UnknownType: scenePath = MENUPATH_UNKNOWN; break; - case Scenes.CustomType: - scenePath = TypeItem.SysAssExtPath; break; + case Scenes.CustomExtension: + scenePath = GetSysAssExtPath(CurrentExtension); break; + case Scenes.PerceivedType: + scenePath = GetSysAssExtPath(CurrentPerceivedType); break; + case Scenes.DirectoryType: + if(CurrentDirectoryType == null) scenePath = null; + else scenePath = GetSysAssExtPath($"Directory.{CurrentDirectoryType}"); break; case Scenes.CommandStore: //Vista系统没有这一项 if(WindowsOsVersion.IsEqualVista) return; this.AddNewItem(RegistryEx.GetParentPath(ShellItem.CommandStorePath)); - this.LoadCommandStoreItems(); + this.LoadStoreItems(); + return; + case Scenes.DragDrop: + this.AddNewItem(MENUPATH_FOLDER); + this.LoadShellExItems(GetShellExPath(MENUPATH_FOLDER)); + this.LoadShellExItems(GetShellExPath(MENUPATH_DIRECTORY)); + this.LoadShellExItems(GetShellExPath(MENUPATH_DRIVE)); + this.LoadShellExItems(GetShellExPath(MENUPATH_ALLOBJECTS)); return; } this.AddNewItem(scenePath); this.LoadItems(scenePath); - - switch(scene) + switch(Scene) { case Scenes.File: - if(WindowsOsVersion.ISAfterOrEqual10) - this.AddItem(new RegRuleItem(RegRuleItem.ShareWithSkype)); + bool flag = WindowsOsVersion.ISAfterOrEqual10; + if(flag) + { + using(RegistryKey key = RegistryEx.GetRegistryKey(@"HKEY_CLASSES_ROOT\PackagedCom\Package")) + { + flag = key != null && key.GetSubKeyNames().ToList().Any(name => name.StartsWith("Microsoft.SkypeApp", StringComparison.OrdinalIgnoreCase)); + } + } + if(flag) this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.ShareWithSkype)); break; case Scenes.Background: - this.AddItem(new RegRuleItem(RegRuleItem.CustomFolder)); + this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.CustomFolder)); break; case Scenes.Computer: - this.AddItem(new RegRuleItem(RegRuleItem.NetworkDrive)); + this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.NetworkDrive)); break; case Scenes.RecycleBin: - this.AddItem(new RegRuleItem(RegRuleItem.RecycleBinProperties)); + this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.RecycleBinProperties)); break; case Scenes.Library: this.LoadItems(MENUPATH_LIBRARY_BACKGROUND); this.LoadItems(MENUPATH_LIBRARY_USER); break; case Scenes.LnkFile: - this.LoadItems(MENUPATH_SYSLNKFILE); + this.LoadItems(GetOpenModePath(".lnk")); break; case Scenes.ExeFile: - this.LoadItems(MENUPATH_SYSEXEFILE); + this.LoadItems(GetOpenModePath(".exe")); break; - case Scenes.CustomType: - this.InsertItem(new TypeItem(), 0); - this.InsertItem(new PerceptionItem(), 1); - this.LoadItems(TypeItem.AssExtPath); + case Scenes.CustomExtension: + case Scenes.PerceivedType: + case Scenes.DirectoryType: + this.InsertItem(new SelectItem(Scene), 0); + if(Scene == Scenes.CustomExtension) this.LoadItems(GetOpenModePath(CurrentExtension)); break; } } private void LoadItems(string scenePath) { - if(this.Scene == Scenes.CustomType && TypeItem.Extension == null) return; + if(scenePath == null) return; RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath); this.LoadShellItems(GetShellPath(scenePath)); this.LoadShellExItems(GetShellExPath(scenePath)); @@ -193,142 +223,293 @@ private void LoadShellExItems(string shellExPath) using(RegistryKey shellExKey = RegistryEx.GetRegistryKey(shellExPath)) { if(shellExKey == null) return; + bool isDragDrop = Scene == Scenes.DragDrop; RegTrustedInstaller.TakeRegTreeOwnerShip(shellExKey.Name); - Dictionary dic = ShellExItem.GetPathAndGuids(shellExPath); + Dictionary dic = ShellExItem.GetPathAndGuids(shellExPath, isDragDrop); + GroupPathItem groupItem = null; + if(isDragDrop) + { + groupItem = GetDragDropGroupItem(shellExPath); + this.AddItem(groupItem); + } foreach(string path in dic.Keys) { string keyName = RegistryEx.GetKeyName(path); if(!names.Contains(keyName)) { - this.AddItem(new ShellExItem(dic[path], path)); + ShellExItem item = new ShellExItem(dic[path], path); + if(groupItem != null) item.FoldGroupItem = groupItem; + this.AddItem(item); names.Add(keyName); } } + if(groupItem != null) groupItem.IsFold = true; } } + private GroupPathItem GetDragDropGroupItem(string shellExPath) + { + string text = null; + Image image = null; + string path = shellExPath.Substring(0, shellExPath.LastIndexOf('\\')); + switch(path) + { + case MENUPATH_FOLDER: + text = AppString.SideBar.Folder; + image = AppImage.Folder; + break; + case MENUPATH_DIRECTORY: + text = AppString.SideBar.Directory; + image = AppImage.Directory; + break; + case MENUPATH_DRIVE: + text = AppString.SideBar.Drive; + image = AppImage.Drive; + break; + case MENUPATH_ALLOBJECTS: + text = AppString.SideBar.AllObjects; + image = AppImage.AllObjects; + break; + } + return new GroupPathItem(shellExPath, ObjectPath.PathType.Registry) { Text = text, Image = image }; + } + private void AddNewItem(string scenePath) { - string shellPath = GetShellPath(scenePath); - NewItem newItem = new NewItem(); + NewItem newItem = new NewItem { Visible = scenePath != null }; this.AddItem(newItem); - if(this.Scene == Scenes.CustomType) + newItem.AddNewItem += (sender, e) => + { + bool isShell; + if(Scene == Scenes.DragDrop) isShell = false; + else + { + using(SelectDialog dlg = new SelectDialog()) + { + dlg.Items = new[] { "Shell", "ShellEx" }; + dlg.Title = "请选择新建菜单类型"; + dlg.Selected = dlg.Items[0]; + if(dlg.ShowDialog() != DialogResult.OK) return; + isShell = dlg.SelectedIndex == 0; + } + } + if(isShell) this.AddNewShellItem(scenePath); + else this.AddNewShellExItem(scenePath); + }; + } + + private void AddNewShellItem(string scenePath) + { + string shellPath = GetShellPath(scenePath); + using(NewShellDialog dlg = new NewShellDialog()) { - newItem.Visible = TypeItem.Extension != null; - TypeItem.ExtensionChanged += (sender, e) => newItem.Visible = TypeItem.Extension != null; + dlg.ScenePath = scenePath; + dlg.ShellPath = shellPath; + if(dlg.ShowDialog() != DialogResult.OK) return; + for(int i = 0; i < this.Controls.Count; i++) + { + if(this.Controls[i] is NewItem) + { + this.InsertItem(new ShellItem(dlg.NewItemRegPath), i + 1); + break; + } + } } - newItem.AddNewItem += (sender, e) => + } + + private void AddNewShellExItem(string scenePath) + { + bool isDragDrop = Scene == Scenes.DragDrop; + using(InputDialog dlg1 = new InputDialog { Title = AppString.Dialog.InputGuid }) { - using(NewShellDialog dlg = new NewShellDialog + if(GuidEx.TryParse(Clipboard.GetText(), out Guid guid)) dlg1.Text = guid.ToString(); + if(dlg1.ShowDialog() != DialogResult.OK) return; + if(GuidEx.TryParse(dlg1.Text, out guid)) { - ScenePath = scenePath, - ShellPath = shellPath - }) + if(isDragDrop) + { + using(SelectDialog dlg2 = new SelectDialog()) + { + dlg2.Title = AppString.Dialog.SelectGroup; + dlg2.Items = new[] { AppString.SideBar.Folder, AppString.SideBar.Directory, + AppString.SideBar.Drive, AppString.SideBar.AllObjects }; + dlg2.Selected = dlg2.Items[0]; + if(dlg2.ShowDialog() != DialogResult.OK) return; + switch(dlg2.SelectedIndex) + { + case 0: + scenePath = MENUPATH_FOLDER; break; + case 1: + scenePath = MENUPATH_DIRECTORY; break; + case 2: + scenePath = MENUPATH_DRIVE; break; + case 3: + scenePath = MENUPATH_ALLOBJECTS; break; + } + } + } + string shellExPath = GetShellExPath(scenePath); + if(ShellExItem.GetPathAndGuids(shellExPath, isDragDrop).Values.Contains(guid)) + { + MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded); + } + else + { + string part = isDragDrop ? ShellExItem.DdhParts[0] : ShellExItem.CmhParts[0]; + string regPath = $@"{shellExPath}\{part}\{guid:B}"; + Registry.SetValue(regPath, "", guid.ToString("B")); + ShellExItem item = new ShellExItem(guid, regPath); + for(int i = 0; i < this.Controls.Count; i++) + { + if(isDragDrop) + { + if(this.Controls[i] is GroupPathItem groupItem) + { + if(groupItem.TargetPath.Equals(shellExPath, StringComparison.OrdinalIgnoreCase)) + { + this.InsertItem(item, i + 1); + item.FoldGroupItem = groupItem; + item.Visible = !groupItem.IsFold; + break; + } + } + } + else + { + if(this.Controls[i] is NewItem) + { + this.InsertItem(item, i + 1); + break; + } + } + } + } + } + else { - if(dlg.ShowDialog() == DialogResult.OK) - this.InsertItem(new ShellItem(dlg.NewItemRegPath), GetItemIndex(newItem) + 1); + MessageBoxEx.Show(AppString.MessageBox.MalformedGuid); } - }; + } } - private void LoadCommandStoreItems() + ///“其他规则”-“公共引用” + private void LoadStoreItems() { using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath)) { Array.ForEach(Array.FindAll(shellKey.GetSubKeyNames(), itemName => !ShellItem.SysStoreItemNames.Contains(itemName, StringComparer.OrdinalIgnoreCase)), itemName => { - this.AddItem(new ShellItem($@"{ShellItem.CommandStorePath}\{itemName}")); + this.AddItem(new StoreShellItem($@"{ShellItem.CommandStorePath}\{itemName}", true, false)); }); } } - sealed class TypeItem : MyListItem + sealed class SelectItem : MyListItem { - static string extension; - public static string Extension + static string selected; + public static string Selected { - get => extension; + get => selected; set { - extension = value; - ExtensionChanged?.Invoke(null, null); + selected = value; + SelectedChanged?.Invoke(null, null); } } - public static string SysAssExtPath => Extension == null ? null : $@"{SYSFILEASSPATH}\{Extension}"; - public static string AssExtPath => Extension == null ? null : $@"HKEY_CLASSES_ROOT\{FileExtension.GetOpenMode(Extension)}"; - public static string ExtensionPath => $@"HKEY_CLASSES_ROOT\{Extension}"; - - public static event EventHandler ExtensionChanged; + public static event EventHandler SelectedChanged; - readonly PictureButton BtnType = new PictureButton(AppImage.Types); + readonly PictureButton BtnSelect = new PictureButton(AppImage.Select); - public TypeItem() + public SelectItem(Scenes scene) { - this.GetText(); - this.Image = AppImage.CustomType; - this.AddCtr(BtnType); this.SetNoClickEvent(); - BtnType.MouseDown += (sender, e) => - { - using(FileExtensionDialog dlg = new FileExtensionDialog()) - if(dlg.ShowDialog() == DialogResult.OK) Extension = dlg.Extension; - }; - ExtensionChanged += (sender, e) => this.GetText(); + this.Image = AppImage.Custom; + this.Text = this.GetText(scene); + this.AddCtr(BtnSelect); + BtnSelect.MouseDown += (sender, e) => Select(scene); } - private void GetText() + private string GetText(Scenes scene) { - if(Extension == null) - { - this.Text = AppString.Dialog.SelectExtension; - } - else + switch(scene) { - this.Text = $"{AppString.Item.CurrentExtension}{Extension}"; + case Scenes.CustomExtension: + if(CurrentExtension == null) + { + return AppString.Item.SelectExtension; + } + else + { + return AppString.Item.CurrentExtension.Replace("%s", CurrentExtension); + } + case Scenes.PerceivedType: + if(CurrentPerceivedType == null) + { + return AppString.Item.SelectPerceivedType; + } + else + { + return AppString.Item.CurrentPerceivedType.Replace("%s", GetPerceivedTypeName()); + } + case Scenes.DirectoryType: + if(CurrentDirectoryType == null) + { + return AppString.Item.SelectDirectoryType; + } + else + { + return AppString.Item.CurrentDirectoryType.Replace("%s", GetDirectoryTypeName()); + } + default: + return null; } } - } - - sealed class PerceptionItem : MyListItem - { - private static readonly string[] PerceptionTypes = { "Text", "Image", "Video", "Audio", "System", "Compressed" }; - - public PerceptionItem() - { - this.Image = ResourceIcon.GetExtensionIcon(TypeItem.Extension)?.ToBitmap() ?? AppImage.NotFound; - this.Text = AppString.Item.SetPerceivedType; - this.Visible = TypeItem.Extension != null; - this.AddCtr(cmbType); - cmbType.Text = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(PerceivedType ?? string.Empty); - cmbType.Items.AddRange(PerceptionTypes); - cmbType.TextChanged += (sneder, e) => PerceivedType = cmbType.Text; - TypeItem.ExtensionChanged += (sender, e) => this.Visible = TypeItem.Extension != null; - } - private static string PerceivedType + private void Select(Scenes scene) { - get => Registry.GetValue(TypeItem.ExtensionPath, "PerceivedType", null)?.ToString(); - set + SelectDialog dlg; + switch(scene) { - if(value.IsNullOrWhiteSpace()) - { - RegistryEx.DeleteValue(TypeItem.ExtensionPath, "PerceivedType"); - } - else - { - Registry.SetValue(TypeItem.ExtensionPath, "PerceivedType", value); - } + case Scenes.CustomExtension: + dlg = new FileExtensionDialog + { + Selected = CurrentExtension?.Substring(1) + }; + break; + case Scenes.PerceivedType: + dlg = new SelectDialog + { + Items = PerceivedTypeNames, + Title = AppString.Item.SelectPerceivedType, + Selected = GetPerceivedTypeName() ?? PerceivedTypeNames[0] + }; + break; + case Scenes.DirectoryType: + dlg = new SelectDialog + { + Items = DirectoryTypeNames, + Title = AppString.Item.SelectDirectoryType, + Selected = GetDirectoryTypeName() ?? DirectoryTypeNames[0] + }; + break; + default: return; + } + if(dlg.ShowDialog() != DialogResult.OK) return; + switch(scene) + { + case Scenes.CustomExtension: + Selected = CurrentExtension = dlg.Selected; + break; + case Scenes.PerceivedType: + Selected = CurrentPerceivedType = PerceivedTypes[dlg.SelectedIndex]; + break; + case Scenes.DirectoryType: + Selected = CurrentDirectoryType = DirectoryTypes[dlg.SelectedIndex]; + break; } } - - readonly ComboBox cmbType = new ComboBox - { - Font = new Font(SystemFonts.MenuFont.FontFamily, 10F), - ImeMode = ImeMode.Disable, - Width = 100.DpiZoom() - }; } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/ShellNewItem.cs b/ContextMenuManager/Controls/ShellNewItem.cs index 8ed08fa..08ae20c 100644 --- a/ContextMenuManager/Controls/ShellNewItem.cs +++ b/ContextMenuManager/Controls/ShellNewItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; @@ -19,7 +19,9 @@ sealed class ShellNewItem : MyListItem, IChkVisibleItem, ITsiTextItem, IBtnShowM ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiRegDeleteItem, ITsiRegExportItem, ITsiCommandItem { public static readonly string[] SnParts = { "ShellNew", "-ShellNew" }; - public static readonly string[] UnableSortExtensions = { ".library-ms", ".lnk", "Folder" }; + public static readonly string[] UnableSortExtensions = { "Folder", ".library-ms" }; + public static readonly string[] DefaultBeforeSeparatorExtensions = { "Folder", ".library-ms", ".lnk" }; + public static readonly string[] EffectValueNames = { "NullFile", "Data", "FileName", "Directory", "Command" }; private static readonly string[] UnableEditDataValues = { "Directory", "FileName", "Handler", "Command" }; private static readonly string[] UnableChangeCommandValues = { "Data", "Directory", "FileName", "Handler" }; @@ -28,7 +30,7 @@ public ShellNewItem(ShellNewList list, string regPath) this.Owner = list; InitializeComponents(); this.RegPath = regPath; - BtnMoveUp.Visible = BtnMoveDown.Visible = this.CanSort && LockNewItem.IsLocked(); + SetSortabled(ShellNewLockItem.IsLocked); } private string regPath; @@ -44,6 +46,7 @@ public string RegPath } } + public string ValueName => null; public string SearchText => $"{AppString.SideBar.New} {Text}"; public string Extension => RegPath.Split('\\')[1]; private string SnKeyName => RegistryEx.GetKeyName(RegPath); @@ -54,10 +57,12 @@ public string RegPath private string OpenModePath => $@"{HKCR}\{OpenMode}";//关联打开方式注册表路径 private string DefaultOpenMode => Registry.GetValue($@"{HKCR}\{Extension}", "", null)?.ToString();//默认关联打开方式 private string DefaultOpenModePath => $@"{HKCR}\{DefaultOpenMode}";//默认关联打开方式注册表路径 + private string ConfigPath => $@"{RegPath}\Config"; + public bool CanSort => !UnableSortExtensions.Contains(Extension, StringComparer.OrdinalIgnoreCase);//能够排序的 private bool CanEditData => UnableEditDataValues.All(value => Registry.GetValue(RegPath, value, null) == null);//能够编辑初始数据的 private bool CanChangeCommand => UnableChangeCommandValues.All(value => Registry.GetValue(RegPath, value, null) == null);//能够更改菜单命令的 - public bool CanSort => !UnableSortExtensions.Contains(Extension, StringComparer.OrdinalIgnoreCase);//能够排序的 + private bool DefaultBeforeSeparator => DefaultBeforeSeparatorExtensions.Contains(Extension, StringComparer.OrdinalIgnoreCase);//默认显示在分割线上不可更改的 public string ItemFilePath { @@ -169,6 +174,34 @@ public string ItemCommand } } + public bool BeforeSeparator + { + get + { + if(DefaultBeforeSeparator) return true; + else return Registry.GetValue(ConfigPath, "BeforeSeparator", null) != null; + } + set + { + if(value) + { + Registry.SetValue(ConfigPath, "BeforeSeparator", ""); + } + else + { + using(RegistryKey snkey = RegistryEx.GetRegistryKey(RegPath, true)) + using(RegistryKey ckey = snkey.OpenSubKey("Config", true)) + { + ckey.DeleteValue("BeforeSeparator"); + if(ckey.GetValueNames().Length == 0 && ckey.GetSubKeyNames().Length == 0) + { + snkey.DeleteSubKey("Config"); + } + } + } + } + } + public ShellNewList Owner { get; private set; } public MoveButton BtnMoveUp { get; set; } public MoveButton BtnMoveDown { get; set; } @@ -185,6 +218,8 @@ public string ItemCommand public ChangeCommandMenuItem TsiChangeCommand { get; set; } readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details); + readonly ToolStripMenuItem TsiOtherAttributes = new ToolStripMenuItem(AppString.Menu.OtherAttributes); + readonly ToolStripMenuItem TsiBeforeSeparator = new ToolStripMenuItem(AppString.Menu.BeforeSeparator); readonly ToolStripMenuItem TsiEditData = new ToolStripMenuItem(AppString.Menu.InitialData); private void InitializeComponents() @@ -205,18 +240,24 @@ private void InitializeComponents() TsiChangeCommand.CommandCanBeEmpty = true; ContextMenuStrip.Items.AddRange(new ToolStripItem[] {TsiChangeText, - new ToolStripSeparator(), TsiChangeIcon, new ToolStripSeparator(), - TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); + new ToolStripSeparator(), TsiChangeIcon, new ToolStripSeparator(), TsiOtherAttributes, + new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); - TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(), - TsiEditData,TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport }); + TsiOtherAttributes.DropDownItems.AddRange(new[] { TsiBeforeSeparator, TsiEditData }); + + TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, + new ToolStripSeparator(), TsiChangeCommand, TsiFileProperties, + TsiFileLocation, TsiRegLocation, TsiRegExport }); - TsiEditData.Click += (sender, e) => EditInitialData(); ContextMenuStrip.Opening += (sender, e) => { TsiEditData.Visible = CanEditData; TsiChangeCommand.Visible = CanChangeCommand; + TsiBeforeSeparator.Enabled = !DefaultBeforeSeparator; + TsiBeforeSeparator.Checked = BeforeSeparator; }; + TsiEditData.Click += (sender, e) => EditInitialData(); + TsiBeforeSeparator.Click += (sender, e) => MoveWithSeparator(!TsiBeforeSeparator.Checked); BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true); BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false); } @@ -235,12 +276,26 @@ private void EditInitialData() } } + public void SetSortabled(bool isLocked) + { + BtnMoveDown.Visible = BtnMoveUp.Visible = isLocked && CanSort; + } + + private void MoveWithSeparator(bool isBefore) + { + BeforeSeparator = isBefore; + ShellNewList list = (ShellNewList)this.Parent; + int index = list.GetItemIndex(list.Separator); + list.SetItemIndex(this, index); + if(ShellNewLockItem.IsLocked) list.SaveSorting(); + } + public void DeleteMe() { RegistryEx.DeleteKeyTree(this.RegPath); RegistryEx.DeleteKeyTree(this.BackupPath); this.Dispose(); - if(LockNewItem.IsLocked()) Owner.WriteRegistry(); + if(ShellNewLockItem.IsLocked) Owner.SaveSorting(); } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/ShellNewList.cs b/ContextMenuManager/Controls/ShellNewList.cs index f9a0e4b..c732acc 100644 --- a/ContextMenuManager/Controls/ShellNewList.cs +++ b/ContextMenuManager/Controls/ShellNewList.cs @@ -1,9 +1,12 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; +using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; using System.Collections.Generic; using System.Linq; +using System.Security.AccessControl; +using System.Security.Principal; using System.Windows.Forms; namespace ContextMenuManager.Controls @@ -11,13 +14,16 @@ namespace ContextMenuManager.Controls sealed class ShellNewList : MyList { public const string ShellNewPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Discardable\PostSetup\ShellNew"; - private static readonly string[] ValueNames = { "NullFile", "Data", "FileName", "Directory", "Command" }; + + public ShellNewSeparator Separator; public void LoadItems() { this.AddNewItem(); - this.AddItem(new LockNewItem(this)); - if(LockNewItem.IsLocked()) this.LoadLockItems(); + this.AddItem(new ShellNewLockItem(this)); + Separator = new ShellNewSeparator(); + this.AddItem(Separator); + if(ShellNewLockItem.IsLocked) this.LoadLockItems(); else this.LoadUnlockItems(); } @@ -44,11 +50,10 @@ private void LoadItems(List extensions) { foreach(string extension in ShellNewItem.UnableSortExtensions) { - string str = extensions.Find(ext => ext.Equals(extension, StringComparison.OrdinalIgnoreCase)); - if(str != null) + if(extensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - extensions.Remove(str); - extensions.Insert(0, str); + extensions.Remove(extension); + extensions.Insert(0, extension); } } using(RegistryKey root = Registry.ClassesRoot) @@ -57,7 +62,7 @@ private void LoadItems(List extensions) { using(RegistryKey extKey = root.OpenSubKey(extension)) { - string defalutOpenMode = extKey.GetValue("")?.ToString(); + string defalutOpenMode = extKey?.GetValue("")?.ToString(); if(string.IsNullOrEmpty(defalutOpenMode)) continue; using(RegistryKey openModeKey = root.OpenSubKey(defalutOpenMode)) { @@ -75,9 +80,18 @@ private void LoadItems(List extensions) if(tKey != null) snPart = $@"{defalutOpenMode}\{snPart}"; using(RegistryKey snKey = extKey.OpenSubKey(snPart)) { - if(ValueNames.Any(valueName => snKey?.GetValue(valueName) != null)) + if(ShellNewItem.EffectValueNames.Any(valueName => snKey?.GetValue(valueName) != null)) { - this.AddItem(new ShellNewItem(this, snKey.Name)); + ShellNewItem item = new ShellNewItem(this, snKey.Name); + if(item.BeforeSeparator) + { + int index2 = this.GetItemIndex(Separator); + this.InsertItem(item, index2); + } + else + { + this.AddItem(item); + } break; } } @@ -88,45 +102,32 @@ private void LoadItems(List extensions) } } - public void MoveItem(ShellNewItem item, bool isUp) + public void MoveItem(ShellNewItem shellNewItem, bool isUp) { - int index = this.GetItemIndex(item); - int firstIndex = 0; - for(int i = 0; i < this.Controls.Count; i++) - { - Control ctr = this.Controls[i]; - if(ctr.GetType() == typeof(ShellNewItem) && ((ShellNewItem)ctr).CanSort) - { - firstIndex = i; break; - } - } - if(isUp) - { - if(index > firstIndex) - { - this.SetItemIndex(item, index - 1); - } - } - else + int index = this.GetItemIndex(shellNewItem); + index += isUp ? -1 : 1; + if(index == this.Controls.Count) return; + Control ctr = this.Controls[index]; + if(ctr is ShellNewItem item && item.CanSort) { - if(index < this.Controls.Count - 1) - { - this.SetItemIndex(item, index + 1); - } + this.SetItemIndex(shellNewItem, index); + this.SaveSorting(); } - this.WriteRegistry(); } - public void WriteRegistry() + public void SaveSorting() { List extensions = new List(); for(int i = 2; i < this.Controls.Count; i++) { - extensions.Add(((ShellNewItem)Controls[i]).Extension); + if(Controls[i] is ShellNewItem item) + { + extensions.Add(item.Extension); + } } - LockNewItem.UnLock(); + ShellNewLockItem.UnLock(); Registry.SetValue(ShellNewPath, "Classes", extensions.ToArray()); - LockNewItem.Lock(); + ShellNewLockItem.Lock(); } private void AddNewItem() @@ -147,9 +148,9 @@ private void AddNewItem() } foreach(Control ctr in this.Controls) { - if(ctr.GetType() == typeof(ShellNewItem)) + if(ctr is ShellNewItem item) { - if(((ShellNewItem)ctr).Extension.Equals(extension, StringComparison.OrdinalIgnoreCase)) + if(item.Extension.Equals(extension, StringComparison.OrdinalIgnoreCase)) { MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded); return; @@ -174,10 +175,109 @@ private void AddNewItem() { item.ItemText = $"{extension.Substring(1)} file"; } - if(LockNewItem.IsLocked()) this.WriteRegistry(); + if(ShellNewLockItem.IsLocked) this.SaveSorting(); } } }; } } + + sealed class ShellNewLockItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiWebSearchItem + { + public ShellNewLockItem(ShellNewList list) + { + this.Owner = list; + this.Image = AppImage.Lock; + this.Text = AppString.Item.LockNewMenu; + this.SetNoClickEvent(); + BtnShowMenu = new MenuButton(this); + ChkVisible = new VisibleCheckBox(this) { Checked = IsLocked }; + MyToolTip.SetToolTip(ChkVisible, AppString.Tip.LockNewMenu); + TsiSearch = new WebSearchMenuItem(this); + this.ContextMenuStrip = new ContextMenuStrip(); + this.ContextMenuStrip.Items.Add(TsiSearch); + } + + public MenuButton BtnShowMenu { get; set; } + public WebSearchMenuItem TsiSearch { get; set; } + public VisibleCheckBox ChkVisible { get; set; } + public ShellNewList Owner { get; private set; } + + public bool ItemVisible + { + get => IsLocked; + set + { + if(value) Owner.SaveSorting(); + else UnLock(); + foreach(Control ctr in Owner.Controls) + { + if(ctr is ShellNewItem item) + { + item.SetSortabled(value); + } + } + } + } + + public string SearchText => Text; + + public static bool IsLocked + { + get + { + using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath)) + { + RegistrySecurity rs = key.GetAccessControl(); + foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount))) + { + if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase)) + { + if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase)) return true; + } + } + } + return false; + } + } + + public static void Lock() + { + using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions)) + { + RegistrySecurity rs = new RegistrySecurity(); + RegistryAccessRule rar = new RegistryAccessRule("Everyone", RegistryRights.Delete | RegistryRights.WriteKey, AccessControlType.Deny); + rs.AddAccessRule(rar); + key.SetAccessControl(rs); + } + } + + public static void UnLock() + { + using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions)) + { + RegistrySecurity rs = key.GetAccessControl(); + foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount))) + { + if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase)) + { + if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase)) + { + rs.RemoveAccessRule(rar); + } + } + } + key.SetAccessControl(rs); + } + } + } + + sealed class ShellNewSeparator : MyListItem + { + public ShellNewSeparator() + { + this.Text = AppString.Item.Separator; + this.HasImage = false; + } + } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/ShellStoreDialog.cs b/ContextMenuManager/Controls/ShellStoreDialog.cs index 68cf709..ced93af 100644 --- a/ContextMenuManager/Controls/ShellStoreDialog.cs +++ b/ContextMenuManager/Controls/ShellStoreDialog.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; using System.Collections.Generic; using System.Drawing; @@ -110,27 +110,30 @@ private void GetSelectedItems() foreach(StoreShellItem item in list.Controls) if(item.IsSelected) SelectedKeyNames.Add(item.KeyName); } + } + } - sealed class StoreShellItem : ShellItem + sealed class StoreShellItem : ShellItem + { + public StoreShellItem(string regPath, bool isPublic, bool isSelect = true) : base(regPath) + { + this.IsPublic = isPublic; + if(isSelect) { - public StoreShellItem(string regPath, bool isPublic) : base(regPath) - { - this.IsPublic = isPublic; - this.AddCtr(chkSelected); - ChkVisible.Visible = BtnSubItems.Visible = false; - RegTrustedInstaller.TakeRegTreeOwnerShip(regPath); - } - public bool IsSelected => chkSelected.Checked; - public bool IsPublic { get; set; } - readonly CheckBox chkSelected = new CheckBox { AutoSize = true }; - - public override void DeleteMe() - { - if(IsPublic && MessageBoxEx.Show(AppString.MessageBox.ConfirmDeleteReferenced, - MessageBoxButtons.YesNo) != DialogResult.Yes) return; - base.DeleteMe(); - } + this.AddCtr(chkSelected, 40.DpiZoom()); + ChkVisible.Visible = BtnShowMenu.Visible = BtnSubItems.Visible = false; } + RegTrustedInstaller.TakeRegTreeOwnerShip(regPath); + } + public bool IsSelected => chkSelected.Checked; + public bool IsPublic { get; set; } + readonly CheckBox chkSelected = new CheckBox { AutoSize = true }; + + public override void DeleteMe() + { + if(IsPublic && MessageBoxEx.Show(AppString.MessageBox.ConfirmDeleteReferenced, + MessageBoxButtons.YesNo) != DialogResult.Yes) return; + base.DeleteMe(); } } } \ No newline at end of file diff --git a/ContextMenuManager/Controls/ShellSubMenuDialog.cs b/ContextMenuManager/Controls/ShellSubMenuDialog.cs index ad28da7..8d1f8a5 100644 --- a/ContextMenuManager/Controls/ShellSubMenuDialog.cs +++ b/ContextMenuManager/Controls/ShellSubMenuDialog.cs @@ -1,9 +1,10 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using System; using System.Collections.Generic; using System.Drawing; +using System.IO; using System.Linq; using System.Reflection; using System.Windows.Forms; @@ -34,23 +35,26 @@ sealed class ShellSubMenuForm : Form { public ShellSubMenuForm() { - this.ShowInTaskbar = this.MinimizeBox = this.MaximizeBox = false; this.StartPosition = FormStartPosition.CenterParent; - this.MinimumSize = this.Size = new Size(646, 389).DpiZoom(); - this.Controls.Add(MlbSubItems); + this.ShowInTaskbar = this.MaximizeBox = this.MinimizeBox = false; + this.MinimumSize = this.Size = new Size(646, 369).DpiZoom(); + this.Controls.AddRange(new Control[] { MlbSubItems, StatusBar }); this.OnResize(null); } /// 子菜单的父菜单的注册表路径 public string ParentPath { get; set; } readonly MyListBox MlbSubItems = new MyListBox { Dock = DockStyle.Fill }; + readonly MyStatusBar StatusBar = new MyStatusBar(); + private MyList LstSubItems; protected override void OnLoad(EventArgs e) { base.OnLoad(e); bool isPublic = true; string value = GetValue(ParentPath, "SubCommands", null)?.ToString(); - if(value.IsNullOrWhiteSpace()) + if(value == null) isPublic = false; + else if(value.IsNullOrWhiteSpace()) { using(var shellKey = RegistryEx.GetRegistryKey($@"{ParentPath}\shell")) { @@ -75,14 +79,27 @@ protected override void OnLoad(EventArgs e) } if(isPublic) { - new PulicMultiItemsList(MlbSubItems).LoadItems(ParentPath); + LstSubItems = new PulicMultiItemsList(MlbSubItems); + ((PulicMultiItemsList)LstSubItems).LoadItems(ParentPath); this.Text += $"({AppString.Dialog.Public})"; } else { - new PrivateMultiItemsList(MlbSubItems).LoadItems(ParentPath); + LstSubItems = new PrivateMultiItemsList(MlbSubItems); + ((PrivateMultiItemsList)LstSubItems).LoadItems(ParentPath); this.Text += $"({AppString.Dialog.Private})"; } + LstSubItems.HoveredItemChanged += (sender, a) => + { + if(!AppConfig.ShowFilePath) return; + MyListItem item = LstSubItems.HoveredItem; + if(item is ITsiFilePathItem pathItem) + { + string path = pathItem.ItemFilePath; + if(File.Exists(path)) { StatusBar.Text = path; return; } + } + StatusBar.Text = item.Text; + }; } sealed class SubMenuModeForm : Form @@ -193,7 +210,7 @@ private void AddNewItem() { if(dlg.ShowDialog() != DialogResult.OK) return; SubKeyNames.Add(dlg.NewItemKeyName); - WriteRegistry(); + SaveSorting(); this.AddItem(new SubShellItem(this, dlg.NewItemKeyName)); } } @@ -210,9 +227,10 @@ private void AddReference() if(dlg.ShowDialog() != DialogResult.OK) return; dlg.SelectedKeyNames.ForEach(keyName => { + if(!SubShellTypeItem.CanAddMore(this)) return; this.AddItem(new SubShellItem(this, keyName)); this.SubKeyNames.Add(keyName); - WriteRegistry(); + SaveSorting(); }); } } @@ -220,11 +238,11 @@ private void AddReference() private void AddSeparator() { this.SubKeyNames.Add("|"); - WriteRegistry(); + SaveSorting(); this.AddItem(new SeparatorItem(this)); } - private void WriteRegistry() + private void SaveSorting() { SetValue(ParentPath, "SubCommands", string.Join(";", SubKeyNames.ToArray())); } @@ -248,7 +266,7 @@ private void MoveItem(MyListItem item, bool isUp) this.SubKeyNames.Reverse(index - 1, 2); } } - this.WriteRegistry(); + this.SaveSorting(); } private void DeleteItem(MyListItem item) @@ -257,7 +275,7 @@ private void DeleteItem(MyListItem item) this.Controls.Remove(item); this.Controls[index - 1].Focus(); this.SubKeyNames.RemoveAt(index - 1); - this.WriteRegistry(); + this.SaveSorting(); item.Dispose(); } @@ -317,6 +335,7 @@ public InvalidItem(PulicMultiItemsList list, string keyName) BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false); MyToolTip.SetToolTip(this, AppString.Tip.InvalidItem); MyToolTip.SetToolTip(BtnDelete, AppString.Menu.Delete); + this.SetNoClickEvent(); } public DeleteButton BtnDelete { get; set; } @@ -347,7 +366,7 @@ public PrivateMultiItemsList(MyListBox owner) : base(owner) /// 父菜单的注册表路径 public string ParentPath { get; set; } /// 子菜单的Shell项注册表路径 - private string ShellPath => $@"{ParentPath}\shell"; + private string ShellPath { get; set; } /// 父菜单的Shell项注册表路径 private string ParentShellPath => RegistryEx.GetParentPath(ParentPath); /// 菜单所处环境注册表路径 @@ -358,6 +377,15 @@ public PrivateMultiItemsList(MyListBox owner) : base(owner) public void LoadItems(string parentPath) { this.ParentPath = parentPath; + string sckValue = GetValue(parentPath, "ExtendedSubCommandsKey", null)?.ToString(); + if(!sckValue.IsNullOrWhiteSpace()) + { + this.ShellPath = $@"HKEY_CLASSES_ROOT\{sckValue}\shell"; + } + else + { + this.ShellPath = $@"{parentPath}\shell"; + } using(var shellKey = RegistryEx.GetRegistryKey(ShellPath)) { if(shellKey == null) return; @@ -420,8 +448,9 @@ private void AddFromParentMenu() if(dlg.ShowDialog() != DialogResult.OK) return; dlg.SelectedKeyNames.ForEach(keyName => { + if(!SubShellTypeItem.CanAddMore(this)) return; string srcPath = $@"{dlg.ShellPath}\{keyName}"; - string dstPath = ObjectPath.GetNewPathWithIndex($@"{this.ShellPath}\{keyName}", ObjectPath.PathType.Registry); + string dstPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry); RegistryEx.CopyTo(srcPath, dstPath); this.AddItem(new SubShellItem(this, dstPath)); @@ -530,12 +559,12 @@ class SubSeparatorItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem public SubSeparatorItem() { this.Text = AppString.Item.Separator; - this.Image = AppImage.Separator; + this.HasImage = false; BtnDelete = new DeleteButton(this); BtnMoveDown = new MoveButton(this, false); BtnMoveUp = new MoveButton(this, true); - MyToolTip.SetToolTip(this, AppString.Tip.Separator); MyToolTip.SetToolTip(BtnDelete, AppString.Menu.Delete); + this.SetNoClickEvent(); } public DeleteButton BtnDelete { get; set; } diff --git a/ContextMenuManager/Controls/ThirdRulesList.cs b/ContextMenuManager/Controls/ThirdRulesList.cs index 15e01a6..aa5a6a5 100644 --- a/ContextMenuManager/Controls/ThirdRulesList.cs +++ b/ContextMenuManager/Controls/ThirdRulesList.cs @@ -1,10 +1,9 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using Microsoft.Win32; using System; using System.IO; -using System.Text; using System.Xml; namespace ContextMenuManager.Controls @@ -27,42 +26,136 @@ public void LoadItems() else continue; } - GroupPathItem groupItem = new GroupPathItem(groupXE.GetAttribute("RegPath"), ObjectPath.PathType.Registry) + GroupPathItem groupItem; + bool isIniGroup = groupXE.HasAttribute("IsIniGroup"); + string attribute = isIniGroup ? "FilePath" : "RegPath"; + ObjectPath.PathType pathType = isIniGroup ? ObjectPath.PathType.File : ObjectPath.PathType.Registry; + groupItem = new GroupPathItem(groupXE.GetAttribute(attribute), pathType) { Text = groupXE.GetAttribute("Text"), - Image = GuidInfo.GetImage(guid), + Image = GuidInfo.GetImage(guid) }; if(groupItem.Text.IsNullOrWhiteSpace()) groupItem.Text = GuidInfo.GetText(guid); this.AddItem(groupItem); + string GetRuleFullRegPath(string regPath) + { + if(string.IsNullOrEmpty(regPath)) regPath = groupItem.TargetPath; + else if(regPath.StartsWith("\\")) regPath = groupItem.TargetPath + regPath; + return regPath; + }; + foreach(XmlElement itemXE in groupXE.ChildNodes) { if(!EnhanceMenusList.JudgeOSVersion(itemXE)) continue; - RegRuleItem.ItemInfo itemInfo = new RegRuleItem.ItemInfo + RuleItem ruleItem; + ItemInfo info = new ItemInfo { Text = itemXE.GetAttribute("Text"), Tip = itemXE.GetAttribute("Tip"), RestartExplorer = itemXE.HasAttribute("RestartExplorer"), }; + int defaultValue = 0, maxValue = 0, minValue = 0; + if(itemXE.HasAttribute("IsNumberItem")) + { + XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule"); + defaultValue = ruleXE.HasAttribute("Default") ? Convert.ToInt32(ruleXE.GetAttribute("Default")) : 0; + maxValue = ruleXE.HasAttribute("Max") ? Convert.ToInt32(ruleXE.GetAttribute("Max")) : int.MaxValue; + minValue = ruleXE.HasAttribute("Min") ? Convert.ToInt32(ruleXE.GetAttribute("Min")) : int.MinValue; + } - XmlNodeList ruleXNList = itemXE.GetElementsByTagName("Rule");//Rules - RegRuleItem.RegRule[] rules = new RegRuleItem.RegRule[ruleXNList.Count]; - for(int i = 0; i < ruleXNList.Count; i++) + if(isIniGroup) { - XmlElement ruleXE = (XmlElement)ruleXNList[i]; - rules[i] = new RegRuleItem.RegRule + XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule"); + string iniPath = ruleXE.GetAttribute("FilePath"); + if(iniPath.IsNullOrWhiteSpace()) iniPath = groupItem.TargetPath; + string section = ruleXE.GetAttribute("Section"); + string keyName = ruleXE.GetAttribute("KeyName"); + if(itemXE.HasAttribute("IsNumberItem")) + { + var rule = new NumberIniRuleItem.IniRule + { + IniPath = iniPath, + Section = section, + KeyName = keyName, + DefaultValue = defaultValue, + MaxValue = maxValue, + MinValue = maxValue + }; + ruleItem = new NumberIniRuleItem(rule, info); + } + else if(itemXE.HasAttribute("IsStringItem")) + { + var rule = new StringIniRuleItem.IniRule + { + IniPath = iniPath, + Secation = section, + KeyName = keyName + }; + ruleItem = new StringIniRuleItem(rule, info); + } + else { - RegPath = ruleXE.GetAttribute("RegPath"), - ValueName = ruleXE.GetAttribute("ValueName"), - TurnOnValue = ruleXE.GetAttribute("On"), - TurnOffValue = ruleXE.GetAttribute("Off"), - ValueKind = GetValueKind(ruleXE.GetAttribute("ValueKind")) - }; - if(string.IsNullOrEmpty(rules[i].RegPath)) rules[i].RegPath = groupItem.TargetPath; - else if(rules[i].RegPath.StartsWith("\\")) rules[i].RegPath = groupItem.TargetPath + rules[i].RegPath; + var rule = new VisbleIniRuleItem.IniRule + { + IniPath = iniPath, + Section = section, + KeyName = keyName, + TurnOnValue = ruleXE.HasAttribute("On") ? ruleXE.GetAttribute("On") : null, + TurnOffValue = ruleXE.HasAttribute("Off") ? ruleXE.GetAttribute("Off") : null, + }; + ruleItem = new VisbleIniRuleItem(rule, info); + } } - this.AddItem(new RegRuleItem(rules, itemInfo) { FoldGroupItem = groupItem, HasImage = false }); + else + { + if(itemXE.HasAttribute("IsNumberItem")) + { + XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule"); + var rule = new NumberRegRuleItem.RegRule + { + RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")), + ValueName = ruleXE.GetAttribute("ValueName"), + ValueKind = GetValueKind(ruleXE.GetAttribute("ValueKind")), + DefaultValue = defaultValue, + MaxValue = maxValue, + MinValue = minValue + }; + ruleItem = new NumberRegRuleItem(rule, info); + } + else if(itemXE.HasAttribute("IsStringItem")) + { + XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule"); + var rule = new StringRegRuleItem.RegRule + { + RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")), + ValueName = ruleXE.GetAttribute("ValueName"), + }; + ruleItem = new StringRegRuleItem(rule, info); + } + else + { + XmlNodeList ruleXNList = itemXE.SelectNodes("Rule"); + var rules = new VisibleRegRuleItem.RegRule[ruleXNList.Count]; + for(int i = 0; i < ruleXNList.Count; i++) + { + XmlElement ruleXE = (XmlElement)ruleXNList[i]; + rules[i] = new VisibleRegRuleItem.RegRule + { + RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")), + ValueName = ruleXE.GetAttribute("ValueName"), + ValueKind = GetValueKind(ruleXE.GetAttribute("ValueKind")), + TurnOnValue = ruleXE.HasAttribute("On") ? ruleXE.GetAttribute("On") : null, + TurnOffValue = ruleXE.HasAttribute("Off") ? ruleXE.GetAttribute("Off") : null, + }; + } + ruleItem = new VisibleRegRuleItem(rules, info); + } + } + this.AddItem(ruleItem); + ruleItem.HasImage = false; + ruleItem.FoldGroupItem = groupItem; } groupItem.IsFold = true; groupItem.HideWhenNoSubItem(); @@ -76,15 +169,18 @@ private XmlDocument ReadXml() XmlDocument doc1 = new XmlDocument(); try { - if(!File.Exists(AppConfig.WebThirdRulesDic)) + if(File.Exists(AppConfig.WebThirdRulesDic)) + { + doc1.LoadXml(File.ReadAllText(AppConfig.WebThirdRulesDic, EncodingType.GetType(AppConfig.WebThirdRulesDic))); + } + else { - File.WriteAllText(AppConfig.WebThirdRulesDic, Properties.Resources.ThirdRulesDic, Encoding.UTF8); + doc1.LoadXml(Properties.Resources.ThirdRulesDic); } - doc1.Load(AppConfig.WebThirdRulesDic); if(File.Exists(AppConfig.UserThirdRulesDic)) { XmlDocument doc2 = new XmlDocument(); - doc2.Load(AppConfig.UserThirdRulesDic); + doc2.LoadXml(File.ReadAllText(AppConfig.UserThirdRulesDic, EncodingType.GetType(AppConfig.UserThirdRulesDic))); foreach(XmlNode xn in doc2.DocumentElement.ChildNodes) { XmlNode node = doc1.ImportNode(xn, true); diff --git a/ContextMenuManager/Controls/WinXGroupItem.cs b/ContextMenuManager/Controls/WinXGroupItem.cs new file mode 100644 index 0000000..e9779a2 --- /dev/null +++ b/ContextMenuManager/Controls/WinXGroupItem.cs @@ -0,0 +1,111 @@ +using BluePointLilac.Methods; +using ContextMenuManager.Controls.Interfaces; +using System.IO; +using System.Windows.Forms; + +namespace ContextMenuManager.Controls +{ + sealed class WinXGroupItem : GroupPathItem, IChkVisibleItem, ITsiDeleteItem, ITsiTextItem + { + public WinXGroupItem(string groupPath) : base(groupPath, ObjectPath.PathType.Directory) + { + InitializeComponents(); + this.TargetPath = groupPath; + } + + public new string TargetPath + { + get => base.TargetPath; + set + { + base.TargetPath = value; + this.Text = Path.GetFileNameWithoutExtension(value); + this.Image = ResourceIcon.GetFolderIcon(value).ToBitmap(); + ChkVisible.Checked = this.ItemVisible; + } + } + + public bool ItemVisible + { + get => (File.GetAttributes(TargetPath) & FileAttributes.Hidden) != FileAttributes.Hidden; + set + { + FileAttributes attributes = File.GetAttributes(TargetPath); + if(value) attributes &= ~FileAttributes.Hidden; + else attributes |= FileAttributes.Hidden; + File.SetAttributes(TargetPath, attributes); + if(Directory.GetFiles(TargetPath).Length > 0) ExplorerRestarter.Show(); + } + } + + public string ItemText + { + get => Path.GetFileNameWithoutExtension(TargetPath); + set + { + string newPath = $@"{WinXList.WinXPath}\{ObjectPath.RemoveIllegalChars(value)}"; + Directory.Move(TargetPath, newPath); + this.TargetPath = newPath; + ExplorerRestarter.Show(); + } + } + + public VisibleCheckBox ChkVisible { get; set; } + public DeleteMeMenuItem TsiDeleteMe { get; set; } + public ChangeTextMenuItem TsiChangeText { get; set; } + readonly ToolStripMenuItem TsiRestoreDefault = new ToolStripMenuItem(AppString.Menu.RestoreDefault); + + private string DefaultGroupPath => $@"{WinXList.DefaultWinXPath}\{ItemText}"; + + private void InitializeComponents() + { + ChkVisible = new VisibleCheckBox(this); + this.SetCtrIndex(ChkVisible, 1); + TsiDeleteMe = new DeleteMeMenuItem(this); + TsiChangeText = new ChangeTextMenuItem(this); + this.ContextMenuStrip = new ContextMenuStrip(); + this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, + new ToolStripSeparator(), TsiRestoreDefault, new ToolStripSeparator(), TsiDeleteMe }); + this.ContextMenuStrip.Opening += (sender, e) => TsiRestoreDefault.Enabled = Directory.Exists(DefaultGroupPath); + TsiRestoreDefault.Click += (sender, e) => RestoreDefault(); + } + + private void RestoreDefault() + { + if(MessageBoxEx.Show(AppString.MessageBox.RestoreDefault, MessageBoxButtons.OKCancel) == DialogResult.OK) + { + File.SetAttributes(TargetPath, File.GetAttributes(DefaultGroupPath)); + string[] paths = Directory.GetFiles(TargetPath); + foreach(string path in paths) + { + File.Delete(path); + } + paths = Directory.GetFiles(DefaultGroupPath); + foreach(string path in paths) + { + File.Copy(path, $@"{TargetPath}\{Path.GetFileName(path)}"); + } + WinXList list = (WinXList)this.Parent; + list.ClearItems(); + list.LoadItems(); + ExplorerRestarter.Show(); + } + } + + public void DeleteMe() + { + bool flag = Directory.GetFiles(TargetPath, "*.lnk").Length > 0; + if(flag && MessageBoxEx.Show(AppString.MessageBox.DeleteGroup, MessageBoxButtons.OKCancel) != DialogResult.OK) return; + File.SetAttributes(TargetPath, FileAttributes.Normal); + Directory.Delete(TargetPath, true); + if(flag) + { + WinXList list = (WinXList)this.Parent; + list.ClearItems(); + list.LoadItems(); + ExplorerRestarter.Show(); + } + else this.Dispose(); + } + } +} \ No newline at end of file diff --git a/ContextMenuManager/Controls/WinXItem.cs b/ContextMenuManager/Controls/WinXItem.cs index 87e1390..0a46aad 100644 --- a/ContextMenuManager/Controls/WinXItem.cs +++ b/ContextMenuManager/Controls/WinXItem.cs @@ -1,5 +1,5 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using ContextMenuManager.Controls.Interfaces; using System.Drawing; using System.IO; @@ -7,8 +7,8 @@ namespace ContextMenuManager.Controls { - sealed class WinXItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, - ITsiTextItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, IFoldSubItem + sealed class WinXItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, IBtnMoveUpDownItem, ITsiAdministratorItem, + ITsiTextItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, IFoldSubItem, ITsiShortcutCommandItem { public WinXItem(string filePath, IFoldGroupItem group) { @@ -24,7 +24,7 @@ public string FilePath set { filePath = value; - this.Shortcut.FullName = value; + this.Shortcut = new WshShortcut(value); this.Text = this.ItemText; this.Image = this.ItemIcon.ToBitmap(); ChkVisible.Checked = this.ItemVisible; @@ -35,8 +35,8 @@ public string ItemText { get { - string name = Shortcut.Description.Trim(); - if(name == string.Empty) name = WinXList.GetItemText(FilePath); + string name = Shortcut.Description?.Trim(); + if(name.IsNullOrWhiteSpace()) name = DesktopIni.GetLocalizedFileNames(FilePath, true); if(name == string.Empty) name = Path.GetFileNameWithoutExtension(FilePath); return name; } @@ -45,7 +45,7 @@ public string ItemText Shortcut.Description = value; Shortcut.Save(); this.Text = ResourceString.GetDirectString(value); - ExplorerRestarter.NeedRestart = true; + ExplorerRestarter.Show(); } } @@ -58,33 +58,39 @@ public bool ItemVisible if(value) attributes &= ~FileAttributes.Hidden; else attributes |= FileAttributes.Hidden; File.SetAttributes(FilePath, attributes); - ExplorerRestarter.NeedRestart = true; + ExplorerRestarter.Show(); } } - private string IconLocation + public Icon ItemIcon { get { - if(Shortcut.IconLocation.StartsWith(",")) - Shortcut.IconLocation = $"{Shortcut.TargetPath}{Shortcut.IconLocation}"; - return Shortcut.IconLocation; + Icon icon = ResourceIcon.GetIcon(Shortcut.IconLocation); + if(icon == null) + { + string path = ItemFilePath; + if(File.Exists(path)) icon = ResourceIcon.GetExtensionIcon(path); + else if(Directory.Exists(path)) icon = ResourceIcon.GetFolderIcon(path); + } + return icon; } } - private WshShortcut Shortcut = new WshShortcut(); - private Icon ItemIcon => ResourceIcon.GetIcon(IconLocation) ?? Icon.ExtractAssociatedIcon(Shortcut.TargetPath); - public string SearchText => $"{AppString.SideBar.WinX} {Text}"; - public string ItemFilePath { get { - if(File.Exists(Shortcut.TargetPath)) return Shortcut.TargetPath; - else return FilePath; + string path = Shortcut.TargetPath; + if(!File.Exists(path) && !Directory.Exists(path)) path = FilePath; + return path; } } + public WshShortcut Shortcut { get; private set; } + public string SearchText => $"{AppString.SideBar.WinX} {Text}"; + private string FileName => Path.GetFileName(FilePath); + public IFoldGroupItem FoldGroupItem { get; set; } public VisibleCheckBox ChkVisible { get; set; } public MenuButton BtnShowMenu { get; set; } @@ -92,29 +98,129 @@ public string ItemFilePath public WebSearchMenuItem TsiSearch { get; set; } public FilePropertiesMenuItem TsiFileProperties { get; set; } public FileLocationMenuItem TsiFileLocation { get; set; } + public ShortcutCommandMenuItem TsiChangeCommand { get; set; } + public RunAsAdministratorItem TsiAdministrator { get; set; } public DeleteMeMenuItem TsiDeleteMe { get; set; } + public MoveButton BtnMoveUp { get; set; } + public MoveButton BtnMoveDown { get; set; } + readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details); + readonly ToolStripMenuItem TsiChangeGroup = new ToolStripMenuItem(AppString.Menu.ChangeGroup); + private void InitializeComponents() { BtnShowMenu = new MenuButton(this); ChkVisible = new VisibleCheckBox(this); + BtnMoveDown = new MoveButton(this, false); + BtnMoveUp = new MoveButton(this, true); TsiChangeText = new ChangeTextMenuItem(this); + TsiChangeCommand = new ShortcutCommandMenuItem(this); + TsiAdministrator = new RunAsAdministratorItem(this); TsiSearch = new WebSearchMenuItem(this); TsiFileLocation = new FileLocationMenuItem(this); TsiFileProperties = new FilePropertiesMenuItem(this); TsiDeleteMe = new DeleteMeMenuItem(this); - ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, - new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); + ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, new ToolStripSeparator(), + TsiChangeGroup, new ToolStripSeparator(), TsiAdministrator, new ToolStripSeparator(), + TsiDetails, new ToolStripSeparator(), TsiDeleteMe }); TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, - new ToolStripSeparator(), TsiFileProperties, TsiFileLocation }); + new ToolStripSeparator(), TsiChangeCommand, TsiFileProperties, TsiFileLocation }); + + TsiChangeGroup.Click += (sender, e) => ChangeGroup(); + BtnMoveDown.MouseDown += (sender, e) => MoveItem(false); + BtnMoveUp.MouseDown += (sender, e) => MoveItem(true); + TsiAdministrator.Click += (sender, e) => { + WinXList.HashLnk(this.FilePath); + ExplorerRestarter.Show(); + }; + TsiChangeCommand.Click += (sender, e) => + { + if(TsiChangeCommand.ChangeCommand(Shortcut)) + { + Image = ItemIcon.ToBitmap(); + WinXList.HashLnk(this.FilePath); + } + }; + } + + private void ChangeGroup() + { + using(SelectDialog dlg = new SelectDialog()) + { + dlg.Title = AppString.Dialog.SelectGroup; + dlg.Items = WinXList.GetGroupNames(); + dlg.Selected = this.FoldGroupItem.Text; + if(dlg.ShowDialog() != DialogResult.OK) return; + if(dlg.Selected == this.FoldGroupItem.Text) return; + string dirPath = $@"{WinXList.WinXPath}\{dlg.Selected}"; + int count = Directory.GetFiles(dirPath, "*.lnk").Length; + string num = (count + 1).ToString().PadLeft(2, '0'); + string partName = this.FileName; + int index = partName.IndexOf(" - "); + if(index > 0) partName = partName.Substring(index + 3); + string lnkPath = $@"{dirPath}\{num} - {partName}"; + lnkPath = ObjectPath.GetNewPathWithIndex(lnkPath, ObjectPath.PathType.File); + string text = DesktopIni.GetLocalizedFileNames(FilePath); + DesktopIni.DeleteLocalizedFileNames(FilePath); + if(text != string.Empty) DesktopIni.SetLocalizedFileNames(lnkPath, text); + File.Move(FilePath, lnkPath); + this.FilePath = lnkPath; + WinXList list = (WinXList)this.Parent; + list.Controls.Remove(this); + for(int i = 0; i < list.Controls.Count; i++) + { + if(list.Controls[i] is WinXGroupItem groupItem && groupItem.Text == dlg.Selected) + { + list.Controls.Add(this); + list.SetItemIndex(this, i + 1); + this.Visible = !groupItem.IsFold; + this.FoldGroupItem = groupItem; + break; + } + } + ExplorerRestarter.Show(); + } + } + + private void MoveItem(bool isUp) + { + WinXList list = (WinXList)this.Parent; + int index = list.Controls.GetChildIndex(this); + if(index == list.Controls.Count - 1) return; + index += isUp ? -1 : 1; + Control ctr = list.Controls[index]; + if(ctr is WinXGroupItem) return; + WinXItem item = (WinXItem)ctr; + string name1 = DesktopIni.GetLocalizedFileNames(this.FilePath); + string name2 = DesktopIni.GetLocalizedFileNames(item.FilePath); + DesktopIni.DeleteLocalizedFileNames(this.FilePath); + DesktopIni.DeleteLocalizedFileNames(item.FilePath); + string fileName1 = $@"{item.FileName.Substring(0, 2)}{this.FileName.Substring(2)}"; + string fileName2 = $@"{this.FileName.Substring(0, 2)}{item.FileName.Substring(2)}"; + string dirPath = Path.GetDirectoryName(this.FilePath); + string path1 = $@"{dirPath}\{fileName1}"; + string path2 = $@"{dirPath}\{fileName2}"; + path1 = ObjectPath.GetNewPathWithIndex(path1, ObjectPath.PathType.File); + path2 = ObjectPath.GetNewPathWithIndex(path2, ObjectPath.PathType.File); + File.Move(this.FilePath, path1); + File.Move(item.FilePath, path2); + if(name1 != string.Empty) DesktopIni.SetLocalizedFileNames(path1, name1); + if(name1 != string.Empty) DesktopIni.SetLocalizedFileNames(path2, name2); + this.FilePath = path1; + item.FilePath = path2; + list.SetItemIndex(this, index); + ExplorerRestarter.Show(); } public void DeleteMe() { - File.Delete(this.FilePath); + File.Delete(FilePath); + DesktopIni.DeleteLocalizedFileNames(FilePath); + ExplorerRestarter.Show(); + this.Shortcut.Dispose(); this.Dispose(); } } diff --git a/ContextMenuManager/Controls/WinXList.cs b/ContextMenuManager/Controls/WinXList.cs index bf211e8..e7ad425 100644 --- a/ContextMenuManager/Controls/WinXList.cs +++ b/ContextMenuManager/Controls/WinXList.cs @@ -1,53 +1,202 @@ -using BulePointLilac.Controls; -using BulePointLilac.Methods; -using ContextMenuManager.Controls.Interfaces; +using BluePointLilac.Controls; +using BluePointLilac.Methods; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Text; +using System.Windows.Forms; namespace ContextMenuManager.Controls { sealed class WinXList : MyList { public static readonly string WinXPath = Environment.ExpandEnvironmentVariables(@"%LocalAppData%\Microsoft\Windows\WinX"); - private static readonly Dictionary DesktopIniReaders = new Dictionary(); + public static readonly string DefaultWinXPath = Environment.ExpandEnvironmentVariables(@"%HOMEDRIVE%\Users\Default\AppData\Local\Microsoft\Windows\WinX"); - public static string GetItemText(string filePath) + public void LoadItems() { - string dirPath = Path.GetDirectoryName(filePath); - string fileName = Path.GetFileName(filePath); - if(DesktopIniReaders.TryGetValue(dirPath, out IniReader reader)) + if(WindowsOsVersion.ISAfterOrEqual8) { - string name = reader.GetValue("LocalizedFileNames", fileName); - name = ResourceString.GetDirectString(name); - return name; + this.AddNewItem(); + this.LoadWinXItems(); } - else return string.Empty; } - public void LoadItems() + private void LoadWinXItems() { - if(WindowsOsVersion.ISAfterOrEqual8) + string[] dirPaths = Directory.GetDirectories(WinXPath); + Array.Reverse(dirPaths); + bool sortable = AppConfig.WinXSortable; + bool sorted = false; + foreach(string dirPath in dirPaths) + { + WinXGroupItem groupItem = new WinXGroupItem(dirPath); + this.AddItem(groupItem); + string[] lnkPaths; + if(sortable) + { + lnkPaths = GetSortedPaths(dirPath, out bool flag); + if(flag) sorted = true; + } + else + { + lnkPaths = Directory.GetFiles(dirPath, "*.lnk"); + Array.Reverse(lnkPaths); + } + foreach(string path in lnkPaths) + { + WinXItem winXItem = new WinXItem(path, groupItem); + winXItem.BtnMoveDown.Visible = winXItem.BtnMoveUp.Visible = sortable; + this.AddItem(winXItem); + } + groupItem.IsFold = true; + } + if(sorted) { - DesktopIniReaders.Clear(); - Array.ForEach(new DirectoryInfo(WinXPath).GetDirectories(), di => LoadSubDirItems(di)); + ExplorerRestarter.Show(); + MessageBoxEx.Show(AppString.MessageBox.WinXSorted); } } - private void LoadSubDirItems(DirectoryInfo di) + private void AddNewItem() { - GroupPathItem groupItem = new GroupPathItem(di.FullName, ObjectPath.PathType.Directory) + NewItem newItem = new NewItem(); + this.AddItem(newItem); + PictureButton btnCreateDir = new PictureButton(AppImage.NewFolder); + MyToolTip.SetToolTip(btnCreateDir, AppString.Tip.CreateGroup); + newItem.AddCtr(btnCreateDir); + btnCreateDir.MouseDown += (sender, e) => CreateNewGroup(); + newItem.AddNewItem += (sender, e) => { - Text = Path.GetFileNameWithoutExtension(di.FullName), - Image = ResourceIcon.GetFolderIcon(di.FullName).ToBitmap() + using(NewLnkFileDialog dlg1 = new NewLnkFileDialog()) + { + if(dlg1.ShowDialog() != DialogResult.OK) return; + using(SelectDialog dlg2 = new SelectDialog()) + { + dlg2.Title = AppString.Dialog.SelectGroup; + dlg2.Items = GetGroupNames(); + dlg2.Selected = dlg2.Items[0]; + if(dlg2.ShowDialog() != DialogResult.OK) return; + string dirPath = $@"{WinXPath}\{dlg2.Selected}"; + string extension = Path.GetExtension(dlg1.ItemFilePath).ToLower(); + string fileName = Path.GetFileNameWithoutExtension(dlg1.ItemFilePath); + int count = Directory.GetFiles(dirPath, "*.lnk").Length; + string index = (count + 1).ToString().PadLeft(2, '0'); + string lnkName = $"{index} - {fileName}.lnk"; + string lnkPath = $@"{dirPath}\{lnkName}"; + WshShortcut shortcut; + if(extension == ".lnk") + { + File.Copy(dlg1.ItemFilePath, lnkPath); + shortcut = new WshShortcut(lnkPath); + } + else + { + shortcut = new WshShortcut(lnkPath) + { + TargetPath = dlg1.ItemFilePath, + Arguments = dlg1.Arguments, + WorkingDirectory = Path.GetDirectoryName(dlg1.ItemFilePath) + }; + } + shortcut.Description = dlg1.ItemText; + shortcut.Save(); + shortcut.Dispose(); + DesktopIni.SetLocalizedFileNames(lnkPath, dlg1.ItemText); + HashLnk(lnkPath); + foreach(MyListItem ctr in this.Controls) + { + if(ctr is WinXGroupItem groupItem && groupItem.Text == dlg2.Selected) + { + WinXItem item = new WinXItem(lnkPath, groupItem) { Visible = !groupItem.IsFold }; + item.BtnMoveDown.Visible = item.BtnMoveUp.Visible = AppConfig.WinXSortable; + this.InsertItem(item, this.GetItemIndex(groupItem) + 1); + break; + } + } + ExplorerRestarter.Show(); + } + } }; - this.AddItem(groupItem); - string iniPath = $@"{di.FullName}\desktop.ini"; - DesktopIniReaders.Add(di.FullName, new IniReader(iniPath)); - Array.ForEach(di.GetFiles(), fi => + } + + private void CreateNewGroup() + { + string dirPath = ObjectPath.GetNewPathWithIndex($@"{WinXPath}\Group", ObjectPath.PathType.Directory, 1); + Directory.CreateDirectory(dirPath); + string iniPath = $@"{dirPath}\desktop.ini"; + File.WriteAllText(iniPath, string.Empty, Encoding.Unicode); + File.SetAttributes(dirPath, File.GetAttributes(dirPath) | FileAttributes.ReadOnly); + File.SetAttributes(iniPath, File.GetAttributes(iniPath) | FileAttributes.Hidden | FileAttributes.System); + this.InsertItem(new WinXGroupItem(dirPath), 1); + } + + public static void HashLnk(string lnkPath) + { + using(FileStream fs = new FileStream(AppConfig.HashLnkExePath, FileMode.OpenOrCreate)) { - if(fi.Extension.ToLower() == ".lnk") this.AddItem(new WinXItem(fi.FullName, groupItem)); - }); + byte[] buffer; + //Any CPU编译条件下,64bit操作系统IntPtr.Size = 8 + if(IntPtr.Size == 8 && !lnkPath.StartsWith(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"))) + { + buffer = Properties.Resources.HashLnk_64; + } + else + { + buffer = Properties.Resources.HashLnk_32; + } + fs.Write(buffer, 0, buffer.Length); + } + + using(Process process = new Process()) + { + process.StartInfo = new ProcessStartInfo + { + FileName = AppConfig.HashLnkExePath, + Arguments = $"\"{lnkPath}\"", + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden + }; + process.Start(); + process.WaitForExit(); + } + } + + public static string[] GetGroupNames() + { + List items = new List(); + DirectoryInfo winxDi = new DirectoryInfo(WinXPath); + Array.ForEach(winxDi.GetDirectories(), di => items.Add(di.Name)); + items.Reverse(); + return items.ToArray(); + } + + private static string[] GetSortedPaths(string groupPath, out bool sorted) + { + sorted = false; + List sortedPaths = new List(); + string[] paths = Directory.GetFiles(groupPath, "*.lnk"); + for(int i = paths.Length - 1; i >= 0; i--) + { + string srcPath = paths[i]; + string name = Path.GetFileName(srcPath); + int index = name.IndexOf(" - "); + if(index >= 2 && int.TryParse(name.Substring(0, index), out int num) && num == i + 1) + { + sortedPaths.Add(srcPath); continue; + } + string dstPath = $@"{groupPath}\{(i + 1).ToString().PadLeft(2, '0')} - {name.Substring(index + 3)}"; + dstPath = ObjectPath.GetNewPathWithIndex(dstPath, ObjectPath.PathType.File); + string value = DesktopIni.GetLocalizedFileNames(srcPath); + DesktopIni.DeleteLocalizedFileNames(srcPath); + if(value != string.Empty) DesktopIni.SetLocalizedFileNames(dstPath, value); + File.Move(srcPath, dstPath); + sortedPaths.Add(dstPath); + sorted = true; + } + return sortedPaths.ToArray(); } } } \ No newline at end of file diff --git a/ContextMenuManager/GuidInfo.cs b/ContextMenuManager/GuidInfo.cs index e7b5d91..dd3b3bb 100644 --- a/ContextMenuManager/GuidInfo.cs +++ b/ContextMenuManager/GuidInfo.cs @@ -1,4 +1,4 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using ContextMenuManager.Controls; using Microsoft.Win32; using System; @@ -27,7 +27,7 @@ public struct IconLocation static GuidInfo() { //将Skype添加到字典 - Guid skypeGuid = new Guid(RegRuleItem.SkypeGuidStr); + Guid skypeGuid = new Guid(VisibleRegRuleItem.SkypeGuid); FilePathDic.Add(skypeGuid, null); ItemTextDic.Add(skypeGuid, AppString.Item.ShareWithSkype); ItemImageDic.Add(skypeGuid, AppImage.Skype); diff --git a/ContextMenuManager/MainForm.cs b/ContextMenuManager/MainForm.cs index 84d4cc5..d1ab8df 100644 --- a/ContextMenuManager/MainForm.cs +++ b/ContextMenuManager/MainForm.cs @@ -1,7 +1,9 @@ -using BulePointLilac.Controls; +using BluePointLilac.Controls; using ContextMenuManager.Controls; +using ContextMenuManager.Controls.Interfaces; using System; using System.Drawing; +using System.IO; using System.Linq; using System.Windows.Forms; @@ -16,21 +18,22 @@ public MainForm() this.ForeColor = Color.FromArgb(80, 80, 80); this.Controls.Add(new ExplorerRestarter()); appSettingBox.Owner = shellList.Owner = shellNewList.Owner = sendToList.Owner = openWithList.Owner - = winXList.Owner = guidBlockedList.Owner = enhanceMenusList.Owner = thirdRuleList.Owner = MainBody; + = winXList.Owner = guidBlockedList.Owner = enhanceMenusList.Owner = thirdRuleList.Owner = iEList.Owner = MainBody; donateBox.Parent = aboutMeBox.Parent = dictionariesBox.Parent = languagesBox.Parent = MainBody; SideBar.SelectIndexChanged += (sender, e) => SwitchItem(); SideBar.HoverIndexChanged += (sender, e) => ShowItemInfo(); ToolBar.SelectedButtonChanged += (sender, e) => SwitchTab(); - ((RefreshButton)ToolBarButtons[3]).ClickMe += (sender, e) => SideBar.SelectIndex = SideBar.SelectIndex; + ToolBarButtons[3].MouseDown += (sender, e) => SwitchItem(); ToolBar.AddButtons(ToolBarButtons); ToolBar.SelectedIndex = 0; + if(AppConfig.ShowFilePath) ShowFilePath(); } readonly MyToolBarButton[] ToolBarButtons = new MyToolBarButton[] { new MyToolBarButton(AppImage.Home, AppString.ToolBar.Home),//主页 new MyToolBarButton(AppImage.Type, AppString.ToolBar.Type),//文件类型 new MyToolBarButton(AppImage.Star, AppString.ToolBar.Rule),//其他规则 - new RefreshButton(),//刷新 + new MyToolBarButton(AppImage.Refresh,AppString.ToolBar.Refresh){ CanBeSelected = false },//刷新 new MyToolBarButton(AppImage.About, AppString.ToolBar.About)//关于 }; readonly ShellList shellList = new ShellList(); @@ -41,6 +44,7 @@ public MainForm() readonly GuidBlockedList guidBlockedList = new GuidBlockedList(); readonly EnhanceMenusList enhanceMenusList = new EnhanceMenusList(); readonly ThirdRulesList thirdRuleList = new ThirdRulesList(); + readonly IEList iEList = new IEList(); readonly ReadOnlyRichTextBox aboutMeBox = new ReadOnlyRichTextBox { Text = AppString.Other.AboutApp @@ -92,51 +96,43 @@ public MainForm() AppString.SideBar.UwpLnk, AppString.SideBar.ExeFile, null, - AppString.SideBar.TextFile, - AppString.SideBar.DocumentFile, - AppString.SideBar.ImageFile, - AppString.SideBar.VideoFile, - AppString.SideBar.AudioFile, - AppString.SideBar.ImageDirectory, - AppString.SideBar.VideoDirectory, - AppString.SideBar.AudioDirectory, + AppString.SideBar.CustomExtension, + AppString.SideBar.PerceivedType, + AppString.SideBar.DirectoryType, null, - AppString.SideBar.UnknownType, - null, - AppString.SideBar.CustomType + AppString.SideBar.UnknownType }; static readonly string[] TypeItemInfos = { AppString.StatusBar.LnkFile, AppString.StatusBar.UwpLnk, AppString.StatusBar.ExeFile, null, - AppString.StatusBar.TextFile, - AppString.StatusBar.DocumentFile, - AppString.StatusBar.ImageFile, - AppString.StatusBar.VideoFile, - AppString.StatusBar.AudioFile, - AppString.StatusBar.ImageDirectory, - AppString.StatusBar.VideoDirectory, - AppString.StatusBar.AudioDirectory, - null, - AppString.StatusBar.UnknownType, + AppString.StatusBar.CustomExtension, + AppString.StatusBar.PerceivedType, + AppString.StatusBar.DirectoryType, null, - AppString.StatusBar.CustomType + AppString.StatusBar.UnknownType }; static readonly string[] OtherRuleItems = { AppString.SideBar.EnhanceMenu, AppString.SideBar.ThirdRules, null, + AppString.SideBar.DragDrop, AppString.SideBar.PublicReferences, - AppString.SideBar.GuidBlocked + AppString.SideBar.GuidBlocked, + null, + AppString.SideBar.IEMenu }; static readonly string[] OtherRuleItemInfos = { AppString.StatusBar.EnhanceMenu, AppString.StatusBar.ThirdRules, null, + AppString.StatusBar.DragDrop, AppString.StatusBar.PublicReferences, - AppString.StatusBar.GuidBlocked + AppString.StatusBar.GuidBlocked, + null, + AppString.StatusBar.IEMenu }; static readonly string[] AboutItems = { @@ -167,18 +163,11 @@ public MainForm() ShellList.Scenes.UwpLnk, ShellList.Scenes.ExeFile, null, - ShellList.Scenes.TextFile, - ShellList.Scenes.DocumentFile, - ShellList.Scenes.ImageFile, - ShellList.Scenes.VideoFile, - ShellList.Scenes.AudioFile, - ShellList.Scenes.ImageDirectory, - ShellList.Scenes.VideoDirectory, - ShellList.Scenes.AudioDirectory, - null, - ShellList.Scenes.UnknownType, + ShellList.Scenes.CustomExtension, + ShellList.Scenes.PerceivedType, + ShellList.Scenes.DirectoryType, null, - ShellList.Scenes.CustomType + ShellList.Scenes.UnknownType }; private void HideAllParts() @@ -245,6 +234,27 @@ private void ShowItemInfo() StatusBar.Text = MyStatusBar.DefaultText; } + private void ShowFilePath() + { + foreach(MyList list in new MyList[] { shellList, shellNewList, sendToList, openWithList, winXList, guidBlockedList, iEList }) + { + list.HoveredItemChanged += (sender, e) => + { + MyListItem item = list.HoveredItem; + if(item is ITsiFilePathItem pathItem) + { + string path = pathItem.ItemFilePath; + if(File.Exists(path)) { StatusBar.Text = path; return; } + } + if(item is GuidBlockedItem guidItem) + { + StatusBar.Text = guidItem.Value; return; + } + StatusBar.Text = item.Text; + }; + } + } + private void SwitchGeneralItem() { switch(SideBar.SelectIndex) @@ -261,6 +271,7 @@ private void SwitchGeneralItem() if(SideBar.SelectIndex <= 9) { shellList.Scene = GeneralShellScenes[SideBar.SelectIndex]; + shellList.LoadItems(); shellList.Visible = true; } break; @@ -270,6 +281,7 @@ private void SwitchGeneralItem() private void SwitchTypeItem() { shellList.Scene = (ShellList.Scenes)TypeShellScenes[SideBar.SelectIndex]; + shellList.LoadItems(); shellList.Visible = true; } @@ -282,9 +294,13 @@ private void SwitchOtherRuleItem() case 1: thirdRuleList.LoadItems(); thirdRuleList.Visible = true; break; case 3: - shellList.Scene = ShellList.Scenes.CommandStore; shellList.Visible = true; break; + shellList.Scene = ShellList.Scenes.DragDrop; shellList.LoadItems(); shellList.Visible = true; break; case 4: + shellList.Scene = ShellList.Scenes.CommandStore; shellList.LoadItems(); shellList.Visible = true; break; + case 5: guidBlockedList.LoadItems(); guidBlockedList.Visible = true; break; + case 7: + iEList.LoadItems(); iEList.Visible = true; break; } } @@ -320,17 +336,5 @@ private void SetSideBarWidth() Array.ForEach(strs, str => maxWidth = Math.Max(maxWidth, SideBar.GetItemWidth(str))); SideBar.Width = maxWidth; } - - sealed class RefreshButton : MyToolBarButton - { - public RefreshButton() : base(AppImage.Refresh, AppString.ToolBar.Refresh) { } - - public EventHandler ClickMe; - - protected override void OnMouseDown(MouseEventArgs e) - { - ClickMe?.Invoke(null, null); - } - } } } \ No newline at end of file diff --git a/ContextMenuManager/Program.cs b/ContextMenuManager/Program.cs index 1503daa..eadefc4 100644 --- a/ContextMenuManager/Program.cs +++ b/ContextMenuManager/Program.cs @@ -1,4 +1,5 @@ -using System; +using BluePointLilac.Methods; +using System; using System.Windows.Forms; namespace ContextMenuManager @@ -12,8 +13,10 @@ namespace ContextMenuManager static class Program { [STAThread] - static void Main() + static void Main(string[] args) { + bool isRestart = args.Length > 0 && args[0] == "Restart"; + if(!isRestart && SingleInstance.IsRunning()) return; Updater.PeriodicUpdate(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); diff --git a/ContextMenuManager/Properties/AssemblyInfo.cs b/ContextMenuManager/Properties/AssemblyInfo.cs index 3403601..4a9241f 100644 --- a/ContextMenuManager/Properties/AssemblyInfo.cs +++ b/ContextMenuManager/Properties/AssemblyInfo.cs @@ -6,10 +6,10 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("蓝点llilac")] [assembly: AssemblyProduct("Windows右键管理")] -[assembly: AssemblyCopyright("Copyright @ 2020 蓝点lilac")] +[assembly: AssemblyCopyright("Copyright @ 2020-2021 蓝点lilac")] [assembly: AssemblyTrademark("蓝点llilac")] [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("35190ec1-2515-488d-a2e9-825d6ff67aa2")] -[assembly: AssemblyVersion("2.2.0.0")] -[assembly: AssemblyFileVersion("2.2.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("3.0.0.0")] +[assembly: AssemblyFileVersion("3.0.0.0")] \ No newline at end of file diff --git a/ContextMenuManager/Properties/Resources.Designer.cs b/ContextMenuManager/Properties/Resources.Designer.cs index 64c5d54..deaee76 100644 --- a/ContextMenuManager/Properties/Resources.Designer.cs +++ b/ContextMenuManager/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace ContextMenuManager.Properties { // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [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 { @@ -47,8 +47,8 @@ internal class Resources { } /// - /// 重写当前线程的 CurrentUICulture 属性 - /// 重写当前线程的 CurrentUICulture 属性。 + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -101,10 +101,10 @@ internal class Resources { } /// - /// 查找类似 ;此文件为ContextMenuManager程序的显示文本字典 - ///;如果你想要帮助作者为此程序添加其他语言字典, 可以修改此文本并保存在.\config\languages文件夹内, - ///;比如美国英语字典保存为en-US.ini, 并给[General]\Language赋值 en-US English - ///;可以在Github或Gitee上Fork该项目并提交申请给我,或者直接发送文件到邮箱1617859183@qq.com + /// 查找类似 ;此文件为 ContextMenuManager Windows右键管理程序 的显示文本字典 + ///;翻译:可帮助作者为此程序提供翻译并提交到Github,以下内容中等号右侧内容替换为翻译文本, + ///;General-Translator为翻译贡献者,General-Language为语言名称,如en-US 美国英语 + ///;翻译文件保存在Config\languages目录中,文件名保存为en-US.ini ///;翻译说明:暂时不翻译的值保留为空即可,字典内赋值换行请使用\n进行转义 /// ///[General] @@ -125,7 +125,8 @@ internal class Resources { ///Directory = 目录 ///Background = 目录背景 ///Desktop = 桌面背景 - ///Drive = 磁盘 [字符串的其余部分被截断]"; 的本地化字符串。 + ///Drive = 磁盘分区 + ///AllObjects [字符串的其余部分被截断]"; 的本地化字符串。 /// internal static string AppLanguageDic { get { @@ -136,9 +137,9 @@ internal class Resources { /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// - internal static System.Drawing.Bitmap CustomType { + internal static System.Drawing.Bitmap Custom { get { - object obj = ResourceManager.GetObject("CustomType", resourceCulture); + object obj = ResourceManager.GetObject("Custom", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } @@ -167,16 +168,14 @@ internal class Resources { /// 查找类似 <?xml version='1.0' encoding='utf-8' ?> ///<!--此文件为常用右键菜单字典, ///Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开始, 子元素Value表示该项的注册表键值,目前仅支持REG_SZ、REG_DWORD、REG_EXPAND_SZ的键值类型, - ///子元素SubKey的所有子元素是该项的子项,项名即为元素名; 每一Item项和SubKey的所有子元素的属性Default为该注册表项默认值,不放在Value\REG_SZ元素里面是为了防止与可能存在的键名为Default的键产生冲突--> + ///子元素SubKey的所有子元素是该项的子项,项名即为元素名; 每一Item项和SubKey的所有子元素的属性Default为该注册表项默认值,不放在Value\REG_SZ元素里面是为了防止与可能存在的键名为Default的键产生冲突 + ///由于Shell项太过复杂,程序只根据注册表项名判断存在即启用,故同一场景下不允许有相同KeyName属性的Shell项目,ShellEx项只要Guid符合则为启用--> ///<Data> /// <File> /// <Shell> - /// <Item KeyName='CopyAsPath' Tip='系统原生菜单项需按住Shift显示,&#x000A;此项可以直接显示'> + /// <Item KeyName='CopyContent' Tip='不需打开文件直接复制文件文本内容&#x000A;非UTF-16 LE(或带BOM)编码会乱码'> /// <Value> - /// <REG_SZ MUIVerb='复制文件路径' Icon='imageres.dll,-5302'/> - /// </Value> - /// <SubKey> - /// [字符串的其余部分被截断]"; 的本地化字符串。 + /// <REG [字符串的其余部分被截断]"; 的本地化字符串。 /// internal static string EnhanceMenusDic { get { @@ -213,6 +212,26 @@ internal class Resources { } } + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] HashLnk_32 { + get { + object obj = ResourceManager.GetObject("HashLnk_32", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// 查找 System.Byte[] 类型的本地化资源。 + /// + internal static byte[] HashLnk_64 { + get { + object obj = ResourceManager.GetObject("HashLnk_64", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// @@ -233,6 +252,16 @@ internal class Resources { } } + /// + /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// + internal static System.Drawing.Bitmap NewFolder { + get { + object obj = ResourceManager.GetObject("NewFolder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// @@ -266,9 +295,9 @@ internal class Resources { /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// - internal static System.Drawing.Bitmap SeparatorItem { + internal static System.Drawing.Bitmap Select { get { - object obj = ResourceManager.GetObject("SeparatorItem", resourceCulture); + object obj = ResourceManager.GetObject("Select", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } @@ -314,13 +343,13 @@ internal class Resources { } /// - /// 查找类似 <?xml version="1.0" encoding="utf-8" ?> + /// 查找类似 <?xml version='1.0' encoding='utf-8' ?> ///<!--每个程序为一个Group,Text为Group项显示文本,Guid用于判断用户是否安装此程序并决定是否显示该Group,不设置Guid则为常驻菜单,RegPath为程序相关注册表主路径; ///其相关菜单项目设置作为一个Item子元素,Item的Text为该Item项显示文本,Tip属性为鼠标悬浮在开关上时的提示信息,需要重启资源管理器生效则添加属性RestartExplorer; - ///Item的子元素Rule为相关注册表内容,RegPath省略则默认为Group主路径,以\开头则为Group主路径的子项路径;ValueName为相关键名,On为启用键值,Off为禁用键值; - ///每个Item可能受多个注册表Rule影响,按照顺序进行键值判定;程序优先判定为On,即只要所有Rule不匹配Off键值就判定为On,键值类型不符时也判定为On; - ///ValueKind为键值类型,默认键值类型ValueKind为REG_DWORD,为默认值时可省略,目前仅支持REG_SZ、REG_DWORD、REG_EXPAND_SZ的键值类型--> - ///< [字符串的其余部分被截断]"; 的本地化字符串。 + ///Item的子元素Rule为相关注册表内容,RegPath省略则默认为Group主路径,以\开头则为Group主路径的子项路径; + ///ValueName为相关键名,On为启用键值,Off为禁用键值;不设置On或Off属性时,其值为null,对应注册表键值不存在; + ///每个Item可能受多个注册表Rule影响,按照顺序进行键值判定;判定规则:当有多条规则时,前面的规则注册表键值匹配On则为On,匹配Off则为Off,并终止判断,都不匹配时继续往下判断,若所有规则都不匹配则为On + ///ValueKind为键值类型,默认键值类型ValueKind为REG_D [字符串的其余部分被截断]"; 的本地化字符串。 /// internal static string ThirdRulesDic { get { @@ -358,16 +387,6 @@ internal class Resources { } } - /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 - /// - internal static System.Drawing.Bitmap Types { - get { - object obj = ResourceManager.GetObject("Types", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - /// /// 查找 System.Drawing.Bitmap 类型的本地化资源。 /// diff --git a/ContextMenuManager/Properties/Resources.resx b/ContextMenuManager/Properties/Resources.resx index 7c15821..a560078 100644 --- a/ContextMenuManager/Properties/Resources.resx +++ b/ContextMenuManager/Properties/Resources.resx @@ -133,8 +133,8 @@ resources\texts\applanguagedic.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - resources\images\customtype.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + resources\images\custom.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a resources\images\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -148,12 +148,21 @@ resources\texts\guidinfosdic.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + resources\hashlnk\hashlnk_32.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + resources\hashlnk\hashlnk_64.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + resources\images\home.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a resources\images\microsoftstore.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + resources\images\newfolder.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + resources\images\newitem.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -163,8 +172,8 @@ resources\images\refresh.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - resources\images\separatoritem.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + resources\images\select.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a resources\images\setting.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -190,9 +199,6 @@ resources\images\type.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - resources\images\types.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - resources\images\up.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/ContextMenuManager/Properties/Resources/HashLnk/HashLnk_32.exe b/ContextMenuManager/Properties/Resources/HashLnk/HashLnk_32.exe new file mode 100644 index 0000000000000000000000000000000000000000..fa2cc317174a9dbef09d2c8f77187ce98f80ba8a GIT binary patch literal 116736 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ì`+1w~!fpwxzZ2R@MilE_g>NZfL-j{)~+Ny3a2L}dF zf1(rJo_5`LnEA4G9}ENN!*S-z(Xj#ea=~ek+LwAsYEgaFDgApq*`aR3*KKvE%NHkN zfH8|<9R0>MGGcEs8D<4VSTcfNC)x?0ZA(o*}abDj}IV$(G8VgIxdIG|9-( zkZCqJsZzJu;3m#J0|Zjrn+uT-eL$TgcIo4}Muv*AJbkHyuR~B@oktu2^)cmZN_3Om^{dh{yc4uBbM&%R>m2A2eX5V8WZ~*!n$sU^f#lz!m5hOAwY+f z$)0j2|8qZ4cv%dqPMLs&uA)4eBcMU-{#YbpLzKBXTRbzx(;9*Zicyqiicuhf1S%Ca z4HvU?lke=@D2IHglhaztaxp24#*u%#63CezFe~htywdt^~souAxxZQxFYCh z62xdGWPW0jXbTjQNFkVv534&M8s0@RfdO?chJryW2||9qAmnw;>oNr$pNN2<)rQYk& z4Sp?st;?1+$oz5y&9dx7OF`BsH*IuCN*N=)*#e`&w%Jw;rNP-5`zr%R$X3aJ21JMT z2r7Y#%o{5M{BKYp=9?V^CHD;Ka%Yjl7U25|u}jyih>Zg(vvl3oS?rK%ExZL{H*G)@ zBlkaVQ781AhmKJ7?DG}2f*VRjXkGfE^~sEUox2PR$UYNB^^^`EmWMkSYmlLje{?JOEA4Eq>(QeSnVQcw0U_2*a=mgT@p3lTqu)nt@1KCqh$2SR zwr0%f=di~+Yic`Km#6cWE)-D5j}+(uCz!}9xwK!Rrg8x0OU-Pq_rRLYtOr(GMyYrV43AlJFxaTy!sGo277C1sWCm5)+f+Di!6xgK0P zU^EOc6QLTn)|-!aNiH4_Z=SWK<_*L9yYc3ws@mGMqCHPCWxw#}|Q)$Ym zzKQSi`(@>^;?5h(nJ!uiifi%GWi%%1Q~lgpQuX2HPn(e3{24tlU1|7R>c%6Gi$_g9 z9(6@{JmANp;VwKjtUm49n2x_?$hI+vY#VElZR5Sjw(&t^+qf3lHf~(q+)H(yX>S4_ zscymW>#xv0&DB0biQ#4v6=}V>XWDxx-|x?~Lj_0(!zawH_Cw=+-}>VW7qb8{0ogSL%%C^6)JQ{7xfmr*#&fMOlO8 zxG^1n%LqY(h{9M9Usr_31AaUj?!sfkY9!o?IE)XEwTQ#`jF+2GtMNKLZ&A0SZ?%}( zPg1dwU1t0gk=D_C6Z0FvayKLwFRm{S|uSU|)6&lUVKFkfF}? zK(x-aIx3|ca!p9ry4>2w{WA_(RWvL~4#gV47J!j}Pc&p>X)ZnT=}|-v0(9wJ^jK|y zIEhY>j!gB9*zjJk=BD8`3CD7PdxV{(Zmla9c!jt%eP`OIp>N7}jP||uGlYS0Ld)Fb2*mtC5mV~Wx@2q$Ryqih1r&aE5_bF_ z(<6#W!d{*Nk(ciWokBRBe~if3pCOuorxcp&iNY0zA3{82G#v$na(eV^9T3JdY~Vkw);P4<=c)j4%v%Rc40 zST3;+_!pZ(^<5e!OHSYwAgl=k=_C|}mM+e}M1;0iX1)PpKf!yVYYBF%K%3tW&P7tb z9i}(`>`UTQ!PY5L*&K>t2O~BiNm!a-ruK`P;Je{uhxt~392`~QI;S$xY8;EATTCIM zsd0U|lRtv?q1&Z`?|>cT3s8!tg*bSal-gAX@pM{ysuoS%?4CP8&2BlW^lkRr=2qBL z2t;<+`df~wr((yaMzwCsN@20g6KAc}hMeU#K?503M_i{MZMXUYnl_keF%; z)Gw7m%OAm6wLJdVsD~bOQ3Bp|9@SKP(2t=ksTYVDMfte(3 za$+En@4Lx-6Fny?6LD1{F6Ll=fL|TI|J$gk1`Tg50yoDxR&<0@ZY+}c1|Z0G>PCnx zCWPi;8i}7zPF4vk3Qg>XK=*Bw5&4wye%^$FT80=DaJ?hq4p09j=B@No(}W9x&kgh; zibQIpf{^TLu_M6za_do5F3ffrr%@-9A>*~~TbHVJI#a%Y0}X4;Y}xm4F0rgrx83@s2xi%95N`M#rMVJKO5aCdRWb(X?%7MFXKTWm(JuOyTSevb0C7MuH0M}vpO}PvY zb#}yNlwbgBvbp&MjQ~E2*+G8&ILusjGUdZjDd|W_cJP88B|6LJQpd15w8mUGpjGF< zs@&*QAWG%}fgwzmVRdVJ-OM)=!WVoF5a;Kg!q&pnJa|y4*$_R0JO|5wl{m;L3>^vq ztArn(sZKZ7Kqf^dx2SC^Oj6q30!(*0{A1Rl8qf$K{b+r+L#Abo*`5=~nYpR@IHcK4 z)h7RpP!MWcUmQ5;dLo3Kz+38Y8GZynXq#^$b)>8HEaqL?;7)P1euhx<21}ZqjDV14 zGY}wY*426#$%QpJuGTID_&jhjh0EQpmS;fr`E~eWtKIx6ygPlzZP0E)`$A~3R(Mbda%JIMTg} zzZd*`LLZ^*4uJ_TK((b;ac@3&6ygjBIgzWm-G-uO4J=jcz>7s-So~srmZdMbNl;eFwn4`MP8OW~xxa z^_glOHdJgcxu&`oYBRN;5Y1HKzX8{dvzT&i?@U(3u|sOUQ<)1ALwxiq%vM2l4lH*N z-(Ldd3xx`*O?4GZzY;0agx#v0c)I6pN?BobE-W^QPgm=94ANYi6W}MX z0BF9Is#qQ%-7pO`yINZ)8S4oU4_4eJ`m+{MP04yo0)Mz375bzJt54 z1i~vpQ4HS>KR=Gu#P3IT9T>b4Z#X>aWl%@u0bVdDIpL5!2h(Vc+R6REsUS~>c#K)~ z^Vh&k@tHRzs6k&YX*`;5Mhi^}RwkcB6;@|!>tsnOCDX!F*kWLR{R%u+;c8j&G~X;V zD>IsJCQS>dm~w?Rq)KeepeN2Cf8i5?aSm3$T(hDU+8;GjuZE}Nfc7_VTzc#W0t&fM zY?^8g(5}bNf6|Lq)l-Ca<(TCmuiCd}?|q#>wm9@0es@Y0OaeKD_Kl_91&)mi*e!0h zz^S$O!?!V|U2INpEO;1R2YG{1kvff-3xe`(u)@H}FrhOG@*&~mI1{>6%@@xsK`Zso z_J%OKd@7VWf_bPSl^a_?yh?*nCO5%h@PR>l6A{Z)Y-Q@*tk&Tf^mZuu#zJ}XMRT*& zi(5`86E}x!bC=uH@!%=IS-?_r3Z~~&!C0Usa3Zl8gXwluVV9MQv7ru1Hu|tB8H19D zwks)~!4eb#Pf?U4mgoxceJ@}JL;PVFI9YkNXAr8e-x5Z?PK2&-b*_4O^IRg1%Eird zucCRJ$ZS`1c)RMy4i@LyT&@2Mcn>ac)G1dLKIDMk4n7N2VB?brKqU_7IQa}909IUY zGbKLcR4=6}ER;_zd?-g9kLtp#IDz&xR)gwX?GblM{p`X=G8?|l7UVz$h<)=)*UP>< zY!Fy}IaEEgf&wpYOX{hB?y+fBI;P3$0U^$9{3MVMsiPnOoFWIcmiNdWoV^gJ_zezf zxrpzwQL4}#Th3^3`ttKXgEN3T$i>mnIborS%nLtH>d^<_=OlFZr?gd3iFB_Ep=Ldd zrC_ejHheE(&jLn*b?_bFwn5}A0*~Fz2T`5aEB5jjg8Di){}V-(yRiUiQ%BEr->XpA zCWlAcW+k5w`;E3YlN|@Wr)<4H1eoDF&500O-UvTMJ=$Hl@Qux6r7!>-$IO$pq>{gb ztwU^#JuK*kow@w&!e!2ex%a(?MRn4;`z{j|0f#pEK#$Io8S@QI-{56 zvi4z}G}!uKO+4|`fF;!LI0ZuZHU3Xnpu@@J*NYh>XYW(|-%y!Zluh0Bf_R3tbEfP2 zzaUsQy1i?`@DPU!6kyJNy`0!i%#)<8?}x%TzXC`h^FIpa${9{U3N4RP*;M)+d@er8 zZ?BJkMiW9TDmB8?9yA;KQDS!a)u;{)t2bEWTIN5ps=JVt1v%zv=xahv{uS6wsL4C{ z=Qk#|j%@f&gWMC_)PzdeO`a32=7xtz{yIkdf05Yi$um?hhaAhv>V`4FA{)nU?*$*3 zvIjyb$eCG>jUUdL2_N*RdXZpn2CQu#xlsx``^L9p_7`n19Kw31?Exz^1O=4(d~MC< z4&uFoiJk%QNgIQc3|d5kU~|B9klDdaQPjtxoa#|FYGRH19F=fbP1wqirApM~hJlP5 z01}At;o2JZ`)aKBDi(wLEg-I@?gFzSRq*?eqZ)R3I31&|Z6w&v|ITGak}_>2%Mfb` zZ8q&iUT?8npXx1kG^9o{;MF78;!*i?M&&PZe=Rdi(|1Cc@(f0#@U!5VgM4k0TT-&j zR0U~TXSUXrt)0uRKLJlZOXQms6@H>~C>=yP_r6Xzl(sFx7sN<%8#OaT(a*A>(Nkl! z2`@^r1%fp6ZlejFW;mW%hY~Cw6b_Y)BiwD#kQ8fVInU&S0EZ<|E07m6t4;6?aFc|g zJriLq9#rQ<(!<@zRmnfQK`aTZ@yif*T*RFij>`xHhHHS!@*rv%OPhAL`M51M86npB zxD92~aSmq(>PGq`=!U$FPFHIHc8RcrG5KHL7GLmI zP1twB{R9Y34+=qtL|(%TDh@Y*hMi6xF_gjPQ&VkT`7CNjPXl4cB9t1Uq7;IHU>=GR zTLfN-HdmOLlG|u3&CGfMz z8o%D~^P@`7qk^;PL7|9)Zz9c1Vz^nfhQI+(HN!o}hxPrmRn4xaFgI;9L7t=D?f{!Z zb2N3I_Q6+B-z?DcjUtElh&m|zdWDMa>cDP7M3VL0+D#^z(kx7XDGj>Y=5th++5^xC zbNzOzf7D`!Dvt2_b{M>HcmS%`?5f-R6&2y8#lt=-qMTA8rlc53I&DPGhuTl?~y;_!aaaGf!`!MO89HyfvyIkmrWNa}&G4I~1pJs!du-G#9D zxDOw(9V>r8+Xgm4$I0oFtp~D2= z<*&hyuwqf3yUHIq7j8!@35TTwGWZQ>loPHKi}I!uG7uV=%3N)I-b_o zHfLdlj8g`L8y%R7%8K9SW0AzhkAzW`lbi?_I`sRTRTci_A?!z(YV$Bq|9|bfFdp^% zuYLEG|IEI-Xek!e|G9lv6w5PiN9ir}xD$V2{H@0ymJ;~@{J&=3?MK(PNXv>O8GqUM zn~A^SzW%R$7aB72f9<;#?j~%E%>19&cefn}+YyMQ)Ic$p-bRkou{3XIBN)f+l!DXb z{233i%iB-llYfdZ#6)dAeM#z!h$EoSp^SX(s9fFW=Sl@rT+lVN(1oTv%sy()2zAZ4)dAE{=TEY4AtaG}~s z@3lBg7P~&cv!Qy0_(bZBi^&nt_yOhSKL-B>H^3Kdy2$P4J>ZZ+5NsiFen=Kb0hIHa zzZuP^k8b{UG~fP_{l9MiDQG&sq5X?4Z2zurY9D^KWn`wBM-Pw2$0*m~3WW@1I?Z5@ zL+ggfvRrX&?vB}K?W@xo-S;Tz^jf3ZXGLd0c|xbW==dE_hz25 zv!~#+s|9L0nH@Bi7BMkVNVl`I;=megX2!fyt?L{PKe9tCFunEYMh8?!HyO8>tiy9u zUpoB^+)a-g3-Vn%yS1~04HGu|+_P=bo>WJ=y}j-!#Ilmvb*|kw#w=?kv(K@mvtv7F zoIPUd8w&7kC!UrthVT$4NWQTrZ^LyHnzuwx8i(KujqX4@Ci;`cy2f=Vw^j-dS7U)$ z8PrRr^R>SR?5pD|wrK77EbiL5kNqyuz0Civd(X4Rq4Rr4XyLZ{h|5+ARriMcN{p%6 z34xsy9+lh%^#Du|X=XZ0kELlY6SRU>d>S<0?)4{YSAgNS&ry23TWha<7DHRk9gnL?qPZH=Wv0K z92IWl)p#=($Ftl4hqQo2c zVkAIg1vxXs>=;$2b~z;QklAn0c$PBS0QB;s}*LnQn8R;(9s+(8uMBoi3MDJt2| zZ$yPnuRVni2`(w38EqSl`>#Rs->G^20eXVN&7?%B^}JQX&vc+5xo=oh*GZ@Y^Ip>? zqMp*`?9Ddd(1gv0H}9EyOc_(W*|zyG?3ka!DOUMbY|Ok>%D8zxB^~h;%Z@DGY@3&_ zoSoaD+7@rN7cbaE@q;k_T9}5~L7w|>g)ytRr%sFCv389)3w~P=3%qXe-n- z&?&oqsU@=ttRdoJe4myHzX4`^Ky|9;L}88F)w&xKz^_7$T@t9g#EY?d5EsUPMvH%*Xr~m$sXpc2}!UW-9 zY%HF+F$bP@xUlex(P3cTBz_8~uo4h7!4beI2FYUoAy`?U_ji`ev+*PFZUFpK&5~@? zvoss$pldqnw1;P1V^mC6(r@GMfaZ5-#hDIPoQdOJIt~>)oT80Iz7E&Uidk$=K>M;p zor-HjdSvW{uGTvMtghl5=>MgfUSVB$^`biM5&JB4qOrh%3~-E> zP>Z-(MvLE|PNvk^s>@0Ru2O335iPz_xk!tDSHUeD55gxjt}0`(^l)H4zFfT%^#H0; ztP%krloY!qaz717xkigC3h;9+P91*vi0VN)DZgUZAVBO?>@oxhaItJLs(f{o zRU-2YV0Y{Y0zu;Naaeuz+A5_Kw5fit7QeUQdV&oG`TzzRe^A|ljCZ2?*gqohKCQ98 z)0XDCDk@pV+yV+e#%RqrFTUJmkdMv$FSYJPT^_ zU9AhyQ*tXAr~{wjIm&8s7X(=c>@T3Ak=)vrJxV_7Xy<7NI3m}^(pr9}jE~G%&i3Qt zqXA@#O<)~II8w*d>5T(yKaO;|b_Qle*45T|-&N$u^5u2j!)gZAkqZ9@$BRVSPH11= ztvDmal&0Wzg%}nfPGjW*c(8;!u%xbp^(KPrtQM6Ih)i;%ctxG}7+AV^oxZ+&5N2~U z&T8_ElI$5Z1;%cohcn(AEXj4^uCK~SZjetB=5Nv}8zoLZ^7$)pw1FM+a}M~hrLfJl z^Az;yA~^C6w%gPRln2)Tz94@WCR`MZ5cRtfxs9dSxCv3gll zkq=h{Y$x9`8)g!si;L}U;J3s2sn^WLa9ulHEm794OeErVgvZ85(z;4!-?^k%T8GG0l!UVEapJA8XTsG? zoN2?xs+Y{h_~192$%^CT`B6+vwQq=5pdb@M74EhfI)Zg`c%EB(A=5ip}9SKB+?Lf1*!hUt=;fzoEfZ2kf;Dios8MeYsNdE0^B zrSw)Uya0X=XYB!EfM*KK-fW+DEeO%l+@b}#`1-EmoVQq!=RKe}<}FpnX?3}0B5Q!@ z*jXO2Z63P-{KeTB@9wbG>VblUqFR7kOeakY;FOqEF^|iKM$P3W3I`@@+BKXb{)w*~w1@YYvhMdel`yn~x-V zm1JPWeON*Cgx`R@4tEIw{&2-s*3l!dn4TW3*P%=iruM|P`aG5tsN@);J%Bdm5Y*;* zn?Te!TRs%K!-{q!qm@+bp0S&O#_)ule_>*p>vg1F=Guwp)UPM*Mzq8Aa?#L_ag){W z5(^*Ak+&b(P@oh-)PwU}WL)9|I|Y_?i4xfGq*RI_v|Wn~MnWuYpMqk$gRa9+wDO z58qISdXSZ5{}_JmL5M>L>gloZb$Hhykh-}fds8~iJGI=waJ%3h3+2Z&?{C7eA-utK zBJ(L=3*lWBad#C@Ck6m}EN%1LB+ljnB2zOR69iO&C$pORTkJ3!&!;q>h?_Wooyt^5 z=kn`IKv|HzGDy(l&j24op;#7@pjZ-Q2X0_plzoE=oG4*v9-FuAn4~LUsfsV)`rmtzkJV~u(CGcq7 zu$*z(D)osP0AeDJ2By)nVx6+l@)Bdmf;AiOu=R|wG%vw zIP~2Ex|qfP0?sLc1a$Q>1g6+L823NMj-Z!wcyWPifLj-C^Mu0}d=TN}irWC-a7A~p z;&3+nZSlfixyc`oS-ZcrLjqGEx(di*Q!2U?I5R9aH}_2kP)3+gx*{v6j3MOe+l}=c z@f)o@P^{Cn5EBmIG7{^~ihL|PiaPfuBqEUkMnJvwIC2`Tl&FhKA@tTB3L7Ws`Q&kW zK12E^{ofDM^R)wb;x3E0co}`-^-Q;TweA(KpGU>(pI^o6=^wQA^g5*aBPXfztV8o6L zr#cAlq#hDtE27ZZ~iUWZFZ^W1o zg&;m?uQ(8yVrH!tqrnY4sqodo%~89j;O3hE+$$Wwc+exbpg52PrA4!rHQZ0s?dZC8gIa^Ku$QKiZ#ywJ80a2XHfZDAp}+;&_-hZ{iOq~jXj@yV+|*oXAgp}5x$yh+&TTII0lg1tgHuu~SgpDC26IayXD=*hA;r%c2_>vIzcglq{pwl@m^3us^2 zm8sfSHf1v3{v{~&t=1yspUtW*SXEQ*e}(O3om5k3#R3i-_C3JxuwnsnCc1+iIl ze**y=3(#;phMUK6K4u+CVy8m))X9i1+g^YcStmQQy@&$u$8I5Rr!kIWZ97M(cPgA} zV^=X8r8va)6wXy-xYZS-f4C1=&!8Ge=6+w#qB{Kit}M(x>Im=vg;<9r(O8oJUJx+D z=7bYT!U-5heYC5=cM-0E*vtB4SbcTXSvd>WYsYgDi!-If`A05aURP*S$F`hsZN=eC zd+cH}9qic{+`U|<+qFG**G_Q>Gk`Y@%A><|USAxkUf%#ps(Xt6I4*Bi#<+Ik?Hd4PZl~&EpVSfk?z*6B7(?XF8d09^0H@!S051d8q&UT~ z5Cut1Il$u7Wp7-)X0XGiUa{FnT>QkyrO+hpJaDC^~gsSbpcY);k)3}g)d zFE0 zTE%^8Uc`a9MEXAsr_Udi?rLd5^@ihL*?TBz%-&`7NBBiRR_~nArA1TVM&ms*_2`+S zR~*J6wZ;LkaC)J!7&ozlD@IoAKSlH_G9LroEA@2#v!8#>mHb%D@rC7OJC}vy$ z-^C8Di4Sy-K0zU{B?#;rriWTzCN6vr>eYR?Car*!RLIavTzT>sK!Dvy@Y?9`eBt>L zfI39r1P?8VqYZ*RuA&Va7op)66u{_qmhSZpDPMT@K*ezk2Uztf*y@wW-H&!g4j91Z zoQ(l_es}Cm&z@u5Ufj=A-OsA~)KsGSzrn2sP{sNNVm~7vVw@}ZGx!#(cnI}Kx?4PP zc^Q59F~FKy%hj2977RvkvHVW-Hk4d0TAw>&#~rT36_%S8f7oyFhr~wo<0=4P`Y?d7 zFfp~{Lx$yM!&wtGL(H3Q1AGNc6Uz{g6t&_PTEKdOCB2aQ>_hF~KF1_#$HlS7ilw6V zV*Hj>mN11MCj)qjW%S zZzkFNuvhqBMFkdxs0crY%MVFi696P&x)Ht%xJ;o4J>WkEr*6(!gdpzbvZ5XnQS(3r zbdkVS5kYK^K8K?DN<;vykRM;eRz@Pd+CGz|^}E_-H7oeKyr)>g?Zso^7BUMZLi$Y> zO$%o)BS8V|F#izLlkOX)8KaX6xcAg@@58mv?bvh4A8J(gOR&2(3L_#+n}O<7PmxSHLh^B~D7c6#=1Wa@uh;qF_0F;F9++M9#J(UHke zr0hC?$%hCSg{+fx7(Dp)MSy~t^)>~|5CnF$5p&zoMw3Z2`7WZ#_x~Epz(*o0>4gM= z-%Tj{I^itz3(e=2;7iHG{EiFa#tlDBjGl(>#y&{u9)wK$T~stI)1IQbuxsyHg0_kt zPuK#%!rU-k_34=H)5B|`{#mRk~TUZ0KbSmKtSj*fK3 zF2)gIYHbXE4YPpTJW}~fVmgHC$ijZy4hZ0Y(ZCfXIB24}f$UQ{?Ql^=9ae32Q5yge zWM9tsVzc8W+%iN8dDyhn&uc*j$88$RcT)DtV~0G0v17aytSrDJM<&w++frySwLgmc zkCn^M(+j3UFBq|fAtV*yU*5IpqUxp(?Vwu81r{V@m^c3?PAQKOa zsBoJB9O}H+gTm~wlUVc-Trylc$IPJ14Mj;@FYZvY=#p_I2T$B6)r_0;C8?<)gNO(| zfJqzIp0>Ozg;1Mgl;iwx5AGhqI4^^+fWdrQi`&=3H6w4sm3)6}c;Y|&MBMaJhJR@;`n~a+_9OruU5x_SYpFS#{et9k#{~3m2^}+S%Er{P$ zhba=ZlOIkpC4W?j*G9!>kBa~KsCYVyxXTaqk*JA{O8E#Zt<`q@+GOoV5vR5Lk+=RH zEsA%)>(Ska+g0QiHPn|Ub3RY8JyWdM8X@?T@LJ8uB@7gR3LzW(3$mm)qp|=x}Z666FM3&bs$U+Id+bK&O zz#+Q+lMAv?LT{6(C7EUI1zA3#EQIxKbx`1mTIhE_&+7{kdg-`$n}wd}%k>v#0p9jb zp)4N>3`Umm7i6J?-T^?rtqy#vXefrS^BO`z?-9ykVJ~Xwz90)Fh+oQUJ1h_!SqSw< z*Fp)sg!OH?5I96bkB(#^%5W_u#O`aeFucFME>T>hQDkgcn}z57^|y>pqsW-2%?-N> z>aD*nk#-f;Ns+NBNu(DP=vqP!x*5vz;}2aS%DO-y~ilF(48Ew z=XiaYYw~gNjQ3FeSQTPVgCpXMje~9`Ga{$J^oTosoWR=zHjg{rI|ux9fUy2PjJS5N zL&ia9-vAWeK4+{LcW%Qz^9Lv;#Z@!Y`PmPNpkI!xq^nBR4w|ux2RrOa`fm}{6mttB zeryay+0Xoe{hp^pK$LWX*r2ug=sl@PPf%TN~puOnpKDEdEI=g zXoPe?{5PjebJ!}4O_~(@P*2cL3PSrGOXGJez@p`o|0wpvBzsp5aZ;Hq?AEpo<7smz z-m`ZJKD}xFj0CAk+EFCj3*5 zfTLK^A?~jfSGSBPGORPu!zMIm{cKpr$WM4mNi36ZgL@Df&JFN10Ec$6tUyFI=d^)0 zHM5R`6-Cs0u?)T(085(Pk{dhyP?2&iKmcueqQps2LdDf)SEB$7+4amX2^YMMmjCp-3kG7yuF}JrsGI9uxvs^Hw57%y#mq|IZL9 zj`}YL=O>Q(Zvk_1!C8MH;rzE}{jqb+V6c=2tpO$b?D6h;^dkCzhGM< z7DoAoGyhYbMB3viXyhUxAxCmyufmUA9XsR^2B*ipLOf64cpw}nFb~j!LXjL!E}F?h z@e?rHxB~IZ;RErhZCAohH=+;=@TusFu$x|u%he#RiTjLjqs|4U@ZlPAFG!<RtpQ0e)AVa!Bl02LGVrWbJaGmo5Wb&EuA~Qr zaNxgnRB`%gr;*}fNOZ%&3#dcPP9VIVN-FfAP$ZR?{P)G58!5hpN^YVDg(7J@eN=H= zH$Yb;jNCPV4&dj`&;h!!Akl&6sPNPDpipEYe}l9Z{|6TqB+mu~`1hf|_`jbf?4&eJ zrz|)pr-|R1tQ;^se^fDH9=v%mSZ*=EU38$YjS&8V3l#pKwO>ARfx@sVBYBAeE%E>S z5(TOcmne{Ufs1XPB>`~G(^$)7pqKfMX=t4UKpb$?)gG@ns;GplBZrP1SVp&4PFERl zx|*CgB)EfLOP!|8FvawpN@liB`sHH$!})r!DOr_ic{`+%*waa&5?m6L>8E)APsx={ z=Bue6WQvYb2e%^DlIwrgg$jg#iVda#*B3+9UySu2_R3kX);1O3R&p{MQ#X%r^b!gU zWcq+IQ4iB$;5Bh(6Nhj#*-W-Cr2aK%W?1T9LM?SfW`=vH2NQ>T@O^R-5r zItj(WUi=*gH~cYo5Lsz-l3wCh=k@T&Pe%8UdSc0uxL=+eZlMztM~Fxx(IZrQr=X9B zKyk>Sseo4$jLiLLr3Zg~em)J&5Yj+Jt3vb}7*<&T)|lyOtjxKE9I$DwCsrixSAgfY ziXWtKnsCLlj{YU-b6(0kyP5_u78%a?WZFpR$M%wE1Bv&5$slMvENEI zvVaxhIM|Fhe9!KdVb|DjeGtB{gE;IFf+P4c+UT6#xH2x{Ufw?@jWi+?SsEPw=(4`F z$X}QDUxweR8P+tB9s+y#n1J{Js!;6P0lpCfB`NkYOghSqqwxgzlW4%tTS{q`kVtAY z(luiv#?DJW?vMbkP;?m^U=~4Hg2k00l!nk$;2hEEjBtQ?jD=DpnSPIDk#_c+a`Wcfr#S}q*pwRv_sHrv+~L8|5@zrU z{QT%4t>@PA{tl)#03C^1% z#rq3VW{a}ghHU&80iNfSw5Fxgn_>^MuFjI_%^Ig`TewY0$0LIKEF*t`Px;_-jJq4X zfmI53=VrhzH3qi;QUAy&i9Y7QCseP&p=IC>>MM$UON--%P6XxHr4-Rsk`2(Bf`%af zSmtQ!DiUX!OYtMguGSzD$vOokp!91j)qDRQ+Maa&5uuNVelZVzB$d3}gHOz|OZZao zsVMZK4T;I+1?frJ+`(6)aL4BAk5R#&sRAr7xRSBQ2x@42yq_PM1$_JkeS-|ptu%B5 zy#DvYR2a&75O$-ypP%a_H@pFV#6i3Ef53$wc-%zp61exMKXHI>c+DlwLgM6x*yMK* z%0g)@3PbG;1t#DAPJ(|HD-JhJLx%$^CAfFA1Vd=^S5T=+Ys$%O1TJ1g@#wb7Jdy}- zyB2HOib1+wc4ngk!;%4O7e56^d0Z|3gbX7|y-(rO@`v5zJ4g3(t#jBmT*o?$a^4;% zb3_GQ;>IUni3%1Azx6Y)iGJ85V&7E#SCk3GCiBAgz|ZaLlCBYz!P&hS-tO?RjT=?* zO};NtEpB#PjUU0fjr=Httgz#Gvk2w)NBAvkhN87uAM^&8*|C7ul47o5-0s^8& z1Bylz4OHTQB*H*cViO{X5dqsHDUG(r12cd}0*RB+Otxb=r_$r8)!M49R@>4l7cW32 z!CZhs0#p+r8WeTMjT$aZ!o|$@d)JdtKi3uG_obYsCzUhd}fk z1CFXB-X%D+y(^X{p{r$fr|pn4`DEoPd)HT(@LsxfGP{)NcKBK5Lzwb%_+MryyPo5Z zcZ-awR4ei+cf4cMkWzv%^~~<7CRr8=Df#*iu8e9j-K^nTbPW^kO6MZm>MMvJhZgbr z+I*n{SJ;ZnhGOxde>IM#$S1OFPa+wYO{yR^xBDt>h0Vx}l*#OoQb0Q!Pe((UZ2H@j zGYbvVpJZ8?)04aPH`ZsvB1{p-OXqNKFjamyYX+u|jK3^+nFYUPpMug}_wnW4WYyrZArx#{92RQQ#sJcVC?2 zF(nty!y7qnxS?7w%T3L39(NC8Tg#HODj4hBiDt>3F7et)NQ(~%cYJ3?UGIArBG0Bk z>u1io<*_J`z%idzfc|fvLJzLj0?gG_ce$78SYz=9F@ zuNZ*eQ6-FM)dxq_3?9vXba!R)EWL0*EB&u~;khr;14nVQx3k+Cln$KlNFj)XCs^u; z^dSXUKl}TA==uYrA@Z)Bl4tVG#DgA;?mZxlaQBNfk%B zn$f~mUCYkx4xOuUx{J(FI(@d}mwN7zddi~pe1dw;IrZe`vg?e3z(dj-<8f4;5=pOn zNP&JE61Rw!sHxH_wL}~ESSpeRDx`s-(FS~x2GF$zgRyPAOP zPAJf~kU=00Juppw#e5DuFiHPamJ=F_%gSr^#Qk)2MRcEBv;m(N0sdRQg|AL!TpNSO7(g&Fm){q!D>(|V+xF>{G z%+(#&f&AHXOPor1f*6}Z%3)bX;5!e;UHY|58Rn0F_FOy)i8j;|*N`GR86>#5L2BK< zno1$Ly^hV-HKwc9$9|dU#ii3K)ZNb*Zj}KYF#Q$2Qcu^?d+8wenDaREO|3Z31uF7{ z)TBQ`u0nkt-%5oPe8}_ zesEzZ{S4e;vD>QGpMo#ETY*y~0FTPo-$VAR*eOFqC1#cJ<0!Bpz5wi!2(b4;6q(v; zo)P(Bs1A~+NdGNgO2m<;32K8X=y60cZIVnzy8^UGH(;><=yJ*kLDvIAuwhZX9wU(b zU9^gX`tPH4zG`p)t#2R^{w7+lC0C(-hHrz=Iz|;8j8;1-18ALYLi@HUWBwXiO{6ZZ zd@`G8=FHaT!V|#^cXKVvjALbJzF12W?cP8yB05!)^Yu$*ekV&2Qy+)L$YmDGu<&HO zfRp7{qDtUXep85>^C6Q@@uDL$S!I6y08{pxnSY8fWgs)_RbnY`3yD~Ks)mLW){3d= z4d~uFwc}c2B_eBwC9%O{%{H#yLa1SjvsFwxou`&3bD#`$c6;K`s4!B}TM~C-k{oNC zD6<-04!YTUk3u^^Q;qe79>m~PbXd4(HNi`*j@Q?{&fYX9G0T;*GF5+jw~!5}TI+{* zK{nKKMdN;?-DV>O-f*BAFF;P0Kc0NlF=50KhCE`Ag!tt}DbObu58RM8#9O-7kWjXy zNdFyIB1N5^Uz3+saV)0xa9l+&znb`fR&+kQ#906|Letu0`qi?0NJIR@*|b{YN(&Ul zOvZk`JH)cAc7-YrTP)pcxjh9t31u_HKOZ&_>N1HzM4XdUU4c5iaqa0Mes%=wm(>HH>~~HIFwp5yciUr1OMz3 zYIR$CX^_AX!uN|4rOT73ZU1SqxP{6GjaIs*uV2q-HbFRZT{$d+o{`smVNW1G7PPNQ zz|BR&Zyr;Q@bgxcdOE}znmyIpVFT0n_Q^J{TlgKWahs=p)y;!m) zEW3cL31m$m>ws847>zdeD8_ZJhHpamlib&8 z^K1kld@#QEy@uH_6U**h?0k2%U90==-jHQBUWEp~S}jF3Z~3TLPijThB#)!aiUK;U z&O07cZG`WhuI{kVmW$#ED6WXU_)Iv`rtT5CXPLE72V4xxF*Hei`Q?`x05--!BV9db z@JPd#7(CLlajJRIXKfTLG6M<(j}7^Hch11z#-o!#&;xUDX)dKGJgj&(9w6_IrZ7lC zta!%i39m7H^Hb5)60d2u+HR>RABDo5bB{-wzflcVu(Z)Ob&`cQ?E}VV$i%?n7)^q6 ztQ}Dn!cxfhbHl( z;$PHm8K)jafU)Ij9@q2drJuOI)5ZeMEvX&#$ z$cmvUeq?f`kf$31qC0rf+`hL0vnjtNjDZZ#7U%H`YqPEO*2WMY=N0HWj4;T##gNI4 zs#(+*Sr-{_qQVM^^HUN3Ud~R4Q>)^@gV8X*du5#kY$J)HqgRKki7?CxWK$A0%}D)m zYBHHGze5)RqpOxOvt_cb38U4 zh$lVHbQL8W9#2-G-ho_PIDFS+HfP;tTbs1nmX)|XzJXBThP65mr4u4FxjC+6g#f`XUDn$1iLKHq=)$5h$J(rCw0x-%ESOeD4=a z_>6Ap7r4*)mR#pU&KuW$t?1Kd)bcRKd`p3!qCx^mWIC@jw{$Kp-fVmSgUG&71a!e| zY`jYdByc-F2}YA$OMu7cjTPNUQ9j`a7?_oUc6_XbJvayg?D}sQ*OC%tdCLmakAsFc zEzqw@bT_G3hisDPe)qUyU9|6zhy?rN^_?oYb4H2sgt}3<^VXmdtJT+JUpXz-QS~_A zr^Ps`9^toG|AorU<>Z=TeVzH-@4l>9ui`me;e+xxR#k-jq(thqwpToa&ym@HDH~#( zdDHzdv^;wer^Dsqud#d0Xvel$Q%+(DxKL_VaKq~Hbg%wY@L`<4MI2HXiK@3-80``Q zN{21zZ+4rjP@#&<1WWmZ;ppj14k3O2DGEnqe{#262&Yok8LjjUs>z| zNb@1vMf2WZE2@`7Qp&cA0)8m26J}fBb{uf5d#Tn2K|ODaIFaif8l%jJCN8z0s-^c5 zy#)J{A^Q7==;IJ2a63P6dn#thM1l7YLjr{7j?u`@h^vi9BfFGB@*_XS#}elv;%rbU z+)otJA7aOp$qhvkH@ZmwZ&9S#vX*Hbmc!wd5>ecM91cDN2z{wGiqIX~!w3a;1I+iM zTx!GEWr|`ZE)n08^vM!{1$#GI7V#p^tLRDz%;Emd731|nRjbJSMP&qA{_pQociT^j z^hQ3(@;@kU_9NOr-84d4o3RfGQ}OnEP&p|2$Ak}5;ZdQuXSsTrp+747^VyA5^p6B? zx9DqIN(3?HNZ#kNkBXC8khet~?qV+>#kl~7BZCP0IvN>7)jT*dh?;hT>$ztD5e24g zF|shYyG%c#hOKsp1*?7-EMNm16ApDXKo_t#I9>J?rX9+KwY_a&oO+K=9@qnf^Wo|Q zr<@i?neVB30%KzC!Iy92>UNMm7b1%EPt`s^tiRX4@PI$B&0kLVIrsf#SCy3F+oWuU z{Pc*7f&i&`C-LKW=t5Zt+Ayjd8E_rQFD@?7U!f``mf+kmh6m;!AUuSg$6V!9f#)ME z3Wx6}pjGo6MY0D#T0G@{t}Q$u5to7lzssoCPG}ryM}wyc31+=nY@hZ()L+=s`%Zed z`#V}aK}`_=VaKgw{0djL)Y<6yTSlG^osr#o%8HFwH%xra;G4OeIp6WLYV(hXy>RzD zeV9n`P*{=aH5^d{V0Lj`;^ullx$8qPhtU)E!a%@%uby_Dt~U4m+MbME5=)0Wj(9)$ zmc}ug%!$!)mNmca!sg!AbW|Kp64m}#C{s5OaLoxU zu$3TS#!~74CQ<~z5~&w*6!BOp4oheMtGV|T%Rl|jw<1S+0W1o}|6rgXYQ9;IABXZ3 z;UQ~{IlV0sS*~ft(6XdEO|eW7bqsOw28dBNWW5mC93Hh8wQ|rY=nAc0j5f#R=>!oju0Zhelr4f z<$C_WOi)8SuR# zZ4@JS?_))4-I`BefEni*&cZ0ve`?mK^>sZfQy|xtVGo{)ExX1a_yze&O0xpuoyK!v zY1Y>FcyLc7MIJmuNfGxo?hErn8_lL9C+IZk7&#W_gqw$3c$U`7uJP5H?U#6dqY+cm zrm0pk>Vkc-&L;PmrK^6IZOI5?(HpSUL2Dp?Gu&#fCyVra^X?ECr|xPn^z>!K7;pady#BIT9OugK>^ z@|iyFND~7%L@lNX97!6{)Rc5YfVx@%AJ16v!LHq$i)UP=UnO#xh-(~|ed6<6zTFdG z2;mn~Pd0Ty(5b;jY4Tml(ruFYbG7+1LH$IiLas>QY4;1Da{LB>6A=Yup6C3=IC%wZ zbZ+fAP#{-(`@T9i*(S*&xJW_uyG{-i(6a5{kmg2Y60#JFid6uY%urzYC}^PXK8)9; zhk)4skn;cg=2(`n!foK8ByQNsGBD#zjqL`w2x4FKw}2-2d_$nCv1L`q7Y#k6IWAEtlP;b zBytVluotT{|D<+HE-~Ab>Q^E6jiqVyO26%Wa`bt3L9}s2;4b;PC@5$va!L1z z#3_dUI~g531vpQ5F4BuOMMz5RM6rIRm8H2Dk%PL8S76ZPiPN{jynz`Y(o9Ei)NP_% zky5!zN#pRI@<6Wswv=iV8zlVP@DY+HH<#a@3K8GMdVbpzC!G2i&og0nG1%&2rJj|YXWBAu(NJ_pAahMQ zz@yTbdsx=WxwvUxnG5OoF9vc${mH)posfPjb0z!2Bxk1k3ciNrsqx5D`%QT&LAP7T zt7ZCC*6vVzWD|>{YsT-zpHrN^PU<6;n;!Z%6t48rCQi95Jk_e^Vy6M=(N*BB=| z)ugxONw%~JJAnF^sN*a1U}kaIS9&V*nHyL`oRr2@V!isNzjcIZuAfhDBc{42NuIkF zlhh^bK`@0YEYW<~0QS*(+l%j%ydzmpPHo_-RCNNdbFi5rX?i!$%RZ?*qpJlhMlg=e z@E%84!b^8MDO+T9JKH>$%k5*EtR(*${5h@}>tBPlH+L5H5@HF{YS(PqQ_5PA#qaj0 zEeR*LTp+iVtAoYA2$rE9YgXlXqoVu^%6#Fzv?e#Es@Xlpv8mOu$!^5UO0nfQcdqEc zd{4OH9|UHPdA%QiD(v=b*{^z8!FnF&22<-1feR-_bGk~wP}X$WWF5W@) z0GVO}a-ZwSB+)Ca$OSKmD`-S(a!;wr!5O?e!?Ec+*@iBtJS`j7^5Hd)P9mFq*2vK} zJ2vISI9pbH%!WkhU0hzej-YaArwVV#wroowJ>Hu`G)ZteZwv zi%YfGz`R)a34vQ;y~K$8vTGwlU!-S9mukfgokc0s6ImM!(Sm|4jbX4SX_=!4-2d8?E>P+_5RT#(ZPIGT z+E_#>rZMYLF=iDt3V*ErDVecnZN#2+SA2siSObFVwT<~^AK`&|CZw8})8y~S%DaqZ37cSgp>{@pX$@Nk4rbsTII6^xk+a@WrSt39 zoEt0NLPQNgSF+71cC`A5)XD|c$1?hr_)p1NnB*1@FRA)-ZwNQRQ6(2(eH|?r^GmZ9 z_Td-nVApR*V^K_m4vx`}?Btw(PB~xs4v=$3W?dw2j;hsU!yO~fAYr`9yjf+IBz>rv zIg-Iql_%M`M>b|OwVo=-=PsyUPrI<9r8leQW?k&g4NZPTZ=nuZxB`w_^bNH$e%s?+ z74$)y^|+1aoUE~rdrp+u3W{{=AAt_HMo&>l*#C{3KP5(ilyy`weJ!}^#!E>q*9gbM zyLhi;GYQRbcx6{sH3S(psiDNI0d!S8Crxn)JwxL(=f9g)RX;(Wqe>n8P3PV{<>HCA zjGWkfLI0nST!kI%%%KYAAI`O`&^P9l;4UcAY_zCk;pU)1i{o`TA>thp6}b`lTxUT3 zFnt7oL2#H_h(-GAR9a#T)BArPog2GwDD-Pd#xbKwSm6B*Pd?X>#bkaL6ok1|=%6dX zAfDnI#o7DWtP8?!!BPMYPJnnzI>m__hWlSN2RqZ9h1&bvhX=0N%HvYFuQ95xZ&Su^ z!zLy^C0a~QoU+Jr0h(+$q zKYj)Hm8&UdocQqZ;QeSK!&agkNU{9j8ndQw0LVwJS2;YI2>nqArtSLAMVzVbUMX5U zyXKwVzUl64Q-pT02F;4IMiXdB9uilK~s@{cNKW3mpBj&1Mcuoxa#Px=E zygZ4IiD>KPcEZrQ3uymhI0D18U11Zg{hWMuZ-ZTVyWo%e8uSA%_comMu0hRvxXO(u zgn|4e-Y@Kd*+@V<9i~hDfxSr>i^#m)7jsD!Pw-N1Z@8UzrJNJr)kkn+FKTn>CBMSL zduS;L$8v|6$4%{o0p3j06k+&kdoYa4oU((?oa0?HS*Co0?xU<+Cn4wZQbO11UrA}R z%T%X4R1>7b=Dt)SLJ=Mz9!q}I`_Gpv-IiJ^<)_K~ktTS1OZ_OM-IsjN?Ku`~N0Y2g zIAM3N@n+@Qr~z-8K@UEHqRc?NDu_bDw|LRGK=#K83khW?$euv38XE26PUj6PKy(T`7M(bGTdd zCvs`+cOY8nPhImJZ~w@(1ob+m2umA&Q*YAWJ}6tfzq}J}u<|#wfE_M$Lya4Wq_4p^Yp5;4om`-~Dbd7pZhv{@Qlp8zLrsAljn z)#TI5pegIa{d>vfFK@#iDGd8+*C#ND{#A6fKV|^2Gy>Q}h=YEcxN+`;Z9^=0?>#m? zd2Ui%A{-34TJ(c-qtWu147#h6ZxQuPqT90}ITaIwC@QL53c$Yd?= zkROc|xh{s3PqT{hPb{8$so@%ZF|0q-s0#;8P$L~AgKDiD`Rk_^9Bf$i-*w59DjJmQ z9I#PS?9}oOpu$e2iFyK+YI!Yw!sFKJpDy8E08*ryf01hVZH?}A!W2YXJxttP%sz85 zw3z!!efNtI(oWp#${h?%c1nW|IRKX zJ8XHhrC%iwUe;AYLU#f7W5~b-R>O7 ztTe5=P(=z4%;{t^hjZ-xZePfMNnq_%|Ki;wHqL3WSP~K%<{U_<_`zTM{EOeIUi==a zSk2g%FD}%JX+bvR0fDjb;5kso7Y3e@c5p<;SPctH3%rd}asP9?NWVnNsTgAz52nc$ z)b}qT^w;f{G;T&0=ySn(*-%mBSo#~mzkUhCc)hw~Vp22jQ%cJ%vR~OWv7FH9Uwui| zpVX#G-cDE_`S!YwKwduCZD z#9)NA+KN%sbbSnb0I^9)&#cYpCUg#xSA?}OuvsyP!XfY93~PQ_PiRE0urbXVymKka zY#gbd2f`XHVhE|95I$I%T*U^C2j&>IUdXTBF+b{u`8D$!Gkwjgu5czRI4{wH-orD3 zeew4-1Eo(8+cV1!l<^W962a8Z!sUMzEw8`zJnbpLq8zv8OmI|vCR@hrBxl?-_zt3( zdKvOZ{NtuL!X{Ka5;~og7RG2BVoN?}O%8$*ITxZ;>aLV0g z%3NpM>Tt@flESH~)?;`bl!%h#q;Q5S%_25u+~jb|1!juf88m^gPC2n% zp<=Q#?%r_9VM&QJ#O=zQWH2CYkBb+j&6?JuabLCU2wv4F7??e_i~(X8d7N4X4fWsZaj85$E{~6hEtj~stl%xQ=~>?NdE4djd3l_p z9$%ElX*>oVd5uS@vVbgsN6fbJ^_jeRcgzYra#TK3_x0+tICGP?aq4ZZywTD~-a?zi zPl>K#dTzQ?9y_*4>fQ3VPd(lvkBvMM(ItYL0{yFfY#+q^%R4JZVfp$DxX_p?BGVi2 zjLMS8tc5rfdyMP28#*@Ms2#^GEH;I>kNu$3*#_}qIty)MFDKDsAO%M#hqKzhiejk< zm4imF8hGsK2D68RHzYoirv8M67mnqyh}RID-AR*~D2k;OlOYvy5!H==?QQyC$uHD@ z+%4T0#jQ>5TU8_mQZ0d7?f&_F&Q_uN-BYGpY&fkLuBZKo)pe2`_u|I*-MiV@hUe?8 zP}LCSBo1wi@92QGZNM2Xe%rNP(yp5am-?mqKp&1tO|GV}aZi6KZDJzFr16Xr%bTRo zh*`43a1*%#5K6-4Zv!TOje#X?be?-p=3n&Pg)};d%%Y<6iOW_wRR9nb^^C2|+VJXN z=j;w;CuzB>IaRIQp%|@NdKrDG82lnMNBBz-T%6nqiz*-Nv%uElCVw705z5DUA5`{5 z1!p7g!4%ms-wACHEe-v45F1@F61D@MbJ`h0JM4P>uw?)JU^pt3iD0ph& zO!5j&fAp5jHz}cC%=Lr`^hkwSJd)$KfgGn*j?jwL`gaBrGkBC@synrAmsFgsJ+h8k zCie3O2Z`j+9(jW2?G^mVXw27p-jpO+bn>S27TP2g=p8(Adkt+;{S!T(lCtrJseL214rj85Pk(01`2O?j#vchkUthcxfun128Pt{h_kA-=L%`dnD# z*xdc5M4Q&$0SgRb`hNH2u+bSfaC@38_z_1d;C%8kL`Fj5UPH<5YlqxZS6rS zH{jc&ApcN7oRjkkU|x*2Kb`2zA5t?d|v?^X{3+gH=Ex?@Xh2GbTjz{Zc0$F zEW7fs%<2UhyU5#ByH@gM?2^2|FL_xUl9yg+d(+G9q6(9^7mj3=P*eqaE(;iD+DU!K zRM|u51vV8Y|Hr)me8%2yadiI=47%|Kr`WH z`fCS@)*AfG=KU~6tCM9D&zf~?<}tF8Vg=|F*?;&=qaWa39%GBN)@_35@Z5jLoz57@ z7>y)2Wg;q4;FiT9xMeH_w~VgVTKOTEX6lof+@APEED92VNouyK0R~awQ>>tXNo{{! zG%t%v*?)0SiSe_0j#7=_X*prBntn9_DxFJyZhD<@t@tO9`#J|QN^WH_B2$GyejS6x z;myH?(?MA#d(uG#8h_D~)q8Z@ASx2#5JiJPUZ;MnirxH7X!Y>QGeBWkn}`rhljiO; z{#_g@98nGx4w()WYMDR6RDy%(I#liIHXSM)t9Bhn3n{a02R72;)n%SS+tfb-A|^x!CrXU(oL08HxUnMSOnQ^(;B{kvgPI zKLWhVhibOBSAAc}uxihta=2Rueiminy|~`2dmdNdVjPWyeZOSk&X;V60)R7|BiI=9C}3jnLvF!OZ^f; zDdg$Y|6TG6?NcEwXY<9B{kw#B9Q=Zh!kST5*Hg83gQv0Rgx{ews@D&&5iTv(@8`x* z0!JlV@y*eHmbd@Vx*vTweG$>gMVKZJUI5LLozeBPcX0yEg zQ#Hf`i?{Nq5Sat(SCG*}G!$AZEV{3IDlDa9|NJNXc^fp7t>pK-rp<~Ug^byH-0xcF z_xvI>Vj5TxUyjvF| zRJ;DFxiNj&0QfT#i4l z#ktq5iW%EBRJkvlx{`#L@#a{~h%XeoNm=)}%k}diA3MM)`GN#6ks@J8s zt0mXk*Y&VqAn&!BJ7OmG)Y;q@*UXB!rsBgh)vlw0Y8)`Ml*Lx_QS@bunUeOA{xH+p zu_^DP(yXz$<8CRjm z$E*2BSPoQlj?6(;MI^#4jJd=vuGZ^2C0`o#3!5NTat89AEucq7$hs~xVz*jZrc_h& zyprL00t-(ub$l_)^{nIJ7J64v{?`Q0Wa7>has&|V@L6?A9XPlAGi3CZKO5tC_-UR( zu2*JLg|nPK1r&m+x*kRy3gfuTHP0bN)Vkl=swJtJS7XbZSv@P^8c}_o|C;lI)t$^! zYi~t)6Lvc<8if=(7Af@gA}asK#vX0{1{s8fA89tVgUBoiKUU0*nP{7BY%nd{{jTSy zE^Lw$>aFkz#d#g@JTH>jzRbV8@WKmh?fPY~pP~n?$qW#C;C>WpguHd7SQ|O8oqfw^ zI5uBWs5>XKS91J#CU|GtV!dY)JR{1I7ZmF0k@S=huHa_d#ra10*=4&7f?}nDcuWS) z;fxn3u0xUD3o}Iy<@xY|KT&+(o(LbfV)Z0UL#F9-U;=8{b0e;9hJ~5De7=&yNJV@y z*}ft7q*0*}Q`InwlQaTubES)RZc9=stb&OP&e@Fc4H3kWE6Uv!9meqTLqK{)*tsFmJJkd^#hray87t1cC|cva92l3SC?Q{^Np^i42)w@ zcoY?LtKS^QRKHJ}vF>E^44A|CvUb|p>hSK6mAg@r0>1oHK+?aQu*wc*F^0sRp=HT; zmHJcA0mESve*^d>4lYAqz09(7J_m3hAoHLg5Rjr7!PdTfEttKW%ev6=)p~ z&n9yeXYn#Upx0%3+%m(&nK+~r+T?j5K20)Ul>Uhr$EI2IWBi&2ch{?+0OgJ9pYm%` z1rG0{f|mD#XX3R6lWOtjb!xV!2MCuR7U_pRhSGqR^Oqk{Qdje{me;8yBtrj1+tB~j zY}N>S87k7}&;XmeNP(jiO@cJqXj}=fTKSP8{TXSXUe64JeA7U6B^teG3h55I7NK<} z$ifi=n6psNVSy9jDdiZ0W>3?PN*Q|I%Wv2O?0mDDuK$vw2RaBMNP62h6%MnOsy0~y z+tl2Ble(-WG_*4L!4NoRH>rNHD%KXvUap)yXL)u18)jdiiDDaR%T~ zXqbT&==lC!l+_lVbv*hOeZ)Qm-<0U}SE6etNa*hRrI}0?`GR5P8$I$V#rOzE0^%va zq>w6Br?4}+9NX&2DLw+P68Sso1Tmib!?jZO2u&Mx1Ml0<^8V+6_fKio__a1S1%u}{ zDq?dhvxw32A zGN<&lJo+bf{SvNQzW!ZaO1i`@BHWFhSt%2;!KZVK`y%}Zs?=Bzpx?qHL)0Y!8Z%r+ z!asTxZh04FHqFzuiuxH5R7zNjGO{y^d_4N}uiJT23 zfd^v5O{7+ip9&8*N&;?atL*;rapL%Ff($bP3MmnhP!#LCv7*&lL`E^c&$rJ}B{G?S z?fgKl-5ESrK(Y1?rr06WT`q34Mg$a-xXdVBSp*bTC7`G`1r+QO=+DZveO{q72t}lS zPZ{i@g663DRMts3H|l)(hb4;gnWjI6EL@pgRW*>|4Y}C+=BK!)YOXXXCCi*Z2S0g< z-^}KM0<;ftaS$o|-AG}dSSxl_@h4#W&Ok@5b=DzfdA*R9MV;oJ#vzejl%aBmz8;CU zH4ag|XtXnO&_V7S1!=4fggbqahCV^2&7`?RphxiInUjgv!_u1lROZIYspcW3`)vUdLvn3zbkv&vBtJy{V~ZT=6|MLtP(eM>&-&%Dt`*5 zf%jm2MkG7=dM5-M;-0@dZMb`jlC6rvJF?Q@MvC{eZRHr?76m%gLgQZliXWyISg>-eXmwFSrJz9W*`lBY0XD-?B|;4p zGuRKAI7^GUhUukrh;|@u`=FnDu8Ou1a^KCB|GVOM#B3*n*q19ZE4zfDa~ledd3`}W z0l_GDq-0AQ>`Zlx!{0n4&%>;zg?C`Yc%DQbEW=}O?e{y4l1wNcG8%^LtW)(jACVPK zqbg|l^vr`svM}F8YbIuuin66jv`yjnZk z$P{)JuLw+}hs*GR75V^_=aj|1+G(W9*Upe9QxKG5S(#C0eYJZL6mLy1*cCr+Mp8w! z?E<9=O?wHusuM1qdg&?~hV+ZYmvo9Q8uyqNhq)XKAX}Aa2C z4{KOustecmc@L}#1#XAV#gUf7m%+Y;7Z0{X0x@~6)s|x}BSF-Q^8>fp{9BLkxO6vp z3j9v}H?7ig3Ra8z!aF_MN7>OLkVg;M{DEkZVt=B(oFXV=HuMs2ZbwRHvva5af(x4$ z_qJ#5TjWnvQn^gLBb)@y;&4HJ{N=j1_q{DAkfdRF5O{u^q!yN0=HVnLhIJH^N2Z0k zc%Z4(PQgOX+-GgU6A$8IZ>zM8v5LU_;1U=~SbN0Wa0%2bS9!SzIXl zOYe&Goh^(vZeHo${6oOmliU1vB1t?p!?%o@!2D z@-~1Kwac5d(WopW=i;IO5S(@8pBu5uV{Vq?=F?b;!BttfTbul(HuuT1j;+Me#fG!f zJ#_meK$6jC#BERGVT=5MmdW5wcYvMw3NAl+2lngQ-u+8D_kWUz@U4Y-zAGHG%?EAP zGkyE@p{~x%9{fyD)Z(oR`RD77>Pa*iu#Eu&_^n_(14;kk&)X5W?Q9^a(x3Mlp=eje zX*qHL)M^Dbq7J&?!I*;}gc(kf+_!+?IvT-+5b*)O1lxKL{PUZ5Jh2Zt3{&BE_kZFo zZ?Xz8=Q+xqi2FL~0s@#9z!JNWs_=mkgtLO(ZO&~neuoyU)7q%D4j;v08GOpW@GZZq z*6)D_L#U54p%quD0^a%Jcp#56hRfd*4MMLcv+G+6k3@d zo-g@@h}L=&^Nva-o#;~NWt4Zmv+T;1L@PYI;xh4mQXirQYPXiRP>*;1Aq#a8!z~nA zv0WlUb<+mWu%SDu{s3OJyxr`vJ;-H?)JX17-lGqJsdb`o2sgU6cS#?7GuK=ET3fAs zcwyQ*{J7Si*#oOR&f6F>ya!g46)81LD@uERbQE68Ax|xooz!0IY*?ASEH0~j%knIm z!2~M_FL(2I$lN-mC7==m(K^p&z5VR)TI(+Vt!KS0;$Ip?y$^-RtPgqTcQcQhas?WR zLZ{8wt?bONF$eJPVy%o&U2*O;PygEb;F<3f9m7Ia#dj|vpp z0;vxN?j2`oWGXS4=Gn5H)@8^2i2#@Eou(ms_Xb1$M9&Ff>c11-7P#8fX6_r1xnmPN z^qhMjwp+dm&H@}Y`F&6D5b)j4-+cX%@nBg!dkIG|?Gn$>2A`WR79%y19<< z;hM<9#XN90iSxJ6TJH<1r5oAAkoGUMcev~w>#Ir7Ha;eQ_xOEP{06&HwV-m)hU*it zQOMjk(?DwzxpNAPRc)&jF`da-`}|v<=WY0L`Y=S6`Dnjy3kiBG_$esp7&}h4Uk;{9 z^y_W~jwXNLNxp(7IZ#7u-5j^{p~8bwkKZSswDnS)R(w*gugCA31a070U+>>InFsF+ zcXKnb%=ABfHE)%tGdW2d4z_k{caX`}Bko@Bzv5!3^&Sc_H3lFM0@ ztZMajlIjiGyr)p?4m`Y0JC1`=wzNQMCT!uYukg*k{uli9o?ecg`Bv%X3frHA@zhL` zo|rBT0c0~ye@cGfSC({4U1Y3T|s?`PP1OE0viB}@9#0yLX?K;YT`k)|3-toK(1 zq~cp{54X%wXxCJajrE%9hNiltDa1g~olmgntx_bj_QcEPw>RZX3wJb;7}0*8jG2+? zU$1^MGHX?ie7_nf^J~B-;{%67s^YpUh8RK$zMw`mvl;({IewMLACTeo2V_2wKV0k{ z-YE94S*+^=CPv`lJ^x@_1qqC+Og8<;a2URATm@Y+u7av>A6Ij9{x{<)C1hO984(>< z0p}l%s|@_t$5r4`<0`$-o|Y`>PYck4$5k3SXIxdw=Z>oaDm<_g@AS27>RZZ3J4aF``6i-{`Vu<%|l*RDo6rqR}3NoISm8hKwei^6r5?dap z^!fT^81LSXE`i?HcJ1%OVR;>}P0w1k{nWp@&G4I9gYXNttMZHyIMx2Dstk9o>!d8f ze!x8|4-s&`GwpA+rXF#j*MNV4vTC}LQZ@J^)$@JmdTmFKKPSs#wz2MUEpS8>4 z1M3y|ied-^8q_XPwT)KYXR2Q?L-%9RN%iU$pCeH<$Pd<8| z52(V~M3zx)n8}*jO*GrXCb`o)?$1`=FC_auOH7q25iY*ZlFfN1H8~r@#DF<});~s? z=Re@zOr}Xk)fU>O+4CX|LVbiAtjpXNty91szAwjUQe~7_-qy^M%-s%-Ly9 z7Xd#!QJ5lE)~PA7K`hRfX=;6CsO4a;HHYsRMnN;z8`vBcM+>&VW6-mH-)d^&9CrO1VPP}Z)WvFgQVf7O5SU=Fqn zsh>BwFhv$oV|Wp13L>?XPCJjG0KyP1l#*)B%Y^h6Fd_YK4PIQ+$k5v+dyC8|*;`~F zWN(@CPxcns*8k4lGJpEO-Xhul{@$X^KX-3&JZjQ|;r&IeTNAf(tbjwoPuKFuHuI3$ z8swA3QT1cB&B)83ZAR!Wwav)Gz%~O_aNOQ8nW#bL6a5WQTh0BBH zlY)a+xO=UprYVzm`R%N1$z@JlV+rDg$i^fUNB5R+AFeU`aD8pp4Kh6=JCS5XU|~2^ zad|T<`Fim$-%=>hwmu<<>kUt;jWUjKF;JLN?d4WD(zu`0Z4EW7VaQMXg zT}TmFft4}lzSuD2I==}uLs`RjAV&t1*$nLJ3j#X@g*vJwA#>Io+8^FTJU-XrywG3^hr=TqD^w3Gk|inC~Wc#7`8DK$Rf3c2$DO zY*-mZ^9kj8bz$HuG(2M!_0{WuUf*H!M4U?(F?gbxkHj zxNEySADCNX$elefv!qjlX4XF&hy!n^PJL|{Hs~@8W+%$*i!=6Q?(0=oh>E&N-1e>2 zsW*HV9-#+5^-u`fp(WO1&W+MzhfnAzoaR?-b@M7pneOBW->M0AkT`t6?&yw7n21Qk zmbP#_8%ax>bPaYQ0pR3vrgJniwS?dIvsu?~yQz zs!G#KYNptMpi%6(1E4YL9mZ>+{sZ&PlUS_#rA6tsG=EPjFN=7BD_N-Dz^*Bg-8|<> zD}y>^Sn-9jKzS`esHl*#;ia!55(_k9~uwlG$fE`(4KKqF{brLrru;F#(hw9MhO zJr^w-z7;J+|6{ydXQE{hMri|RL2FLAoCPh*q{RqYZW+#tV8vrE(uIMgu!7R`09I75 zz{_TdTNP3FRhao1@?$k!TFmLfvQxxNUJ@G$%lEpB?rdD$(c#=tw_ucx^nDy}ML5!* zr_bw9(~_x%m(zawT;sA=iVztve$`k5amGp4lK_ zA3<8QHzJ}uijy>1lUCNAXvY1JMD!nIF|zRNIOw-Kb8YV1HJl(L4(CRI$t1#B_cXkO zy831kNkJs|p%Ifz?Yi-z5zp&n^K~Q^I!;$NYB8t^x=y-duRmB*FY0%L%;Oez{YH3p zBAH*%%7(PplJ&+w!bH|WS^<4hv!{fn%RlEy-P2S0VFrX{K zpd%4T6{SW0M)BU@f4Yyq;^NA`6bp|>}%D#jbKJ02CenWkEC?r}Ww zE8YUBH~7~drF`HP(O5d}_t`1uJht*WEtFq0;2Fic1NDM62^tqw$za55>Q__ESbLqv zR2x=dgdDhh=r3B{1Xd9(K&CQ|eStH?*RNopBrGSq#)oru9rVx>_S5uIf zpabBe>}=3{(mO493wm}uaQr@gToT-YD($gA5;;sXb|pyWje>;_h6@AC0lK+OaegQ|4ByqRUp>i0YQS56OC zsjz1~?(AN9N%5dz8kF74xaOetwBX-tMcXKK8u8}*H8X4V8SjNh{eu;k6c-Kz5?8IR zZ4YNN$6EpGS5-RSUsmE@e}oGCPwUc!6O8@|ZNK)kq%ogr@X`@eGk2sK`S7?KnySGB zDpIo`$$7@%GbpF^cS&*7gr}HO1noNQc?UYtKLBhTrobkY4+XWuescwVWAkP)7( zPBk4?j2>KEb$r$;T?^I3lSX*z7{_zAam$a1Q=J`Yi<;~8 ztUt(Yo?F%vK`m&iITnONr4lT{_usNRD5aS(`epwT-F&8hKNRAe4z2CdB!hC4_W|40 zHc4X+r|N$rM|h`7;Sp=udkZl)%4kJ4?btjyAuu~^XEYnLB7AI(4$LIMb#|paFf(ju zR4Q9gN)z;W4Or3MGs0$hK2$M2l60|Y>p-IIAi*ZQGrlJ z@yRnrziR+A&-Wtz_>l6V5(Uq7j6QWBM`1L_c5)P%9ejVq2t9Qm&$Z;~QtJ9|=1c=$kbxGTm*9 z7vopZ+3miFjmtFCT)lvHUd(pi>Yct0>kf-MN3?CR(6xtMxfnN!pK+WF!=J;Hu5#){ z@@vhktw+RAuPxEN<5fZL68m;3W?+c4o&L(R{udewPc3Co?AT6#lZat^L4laCGKGMk zx3x4q?<^*6rHM`A8NU5Tedpwr^ZaO%SR^JKoY=$q z*#DB}2zew$j2efwf6=bLf2^a8w={QryCEM2z<3JK&S?zwvgq_O-7_dN^iDzD#<8Go zNyLlS4X;DXDfMJXPm4B5Dfvc}n#0x;rbm}VGMKxY@j=j<9jPLzlFuppErEFZNj|ta4K3cn;y@&>h zx*o-y=n~qC^&tukP{cOeT;BdG(c?x`;ThUfAzYXk577@}J!5;a?}>4bEQ{6K=ucS; zzWw$}$54)SHx=vR%0LX&u2!UCxKc2_RhuWk^?TquNrIz0_&VL!h zN-np1L;apnxsT1s-x#zr?R|x_I+(cx5Bd_Hu+b0lI|FreZ7^1?`SdVohDkeEW@xmm2EEh`(Jm=d;W z?@>|2`I4GO`;BXWUt4%c^RyM~f7(Qw_4;hH8ogkk=KJra<~n40PXakRSQug<Nq-q*;i1ujln8k!ii;9Oyit58NRN0k4_j&sf!mg3EY0_M&8tR0x5p9q#g8LMG&kW2v|5jCL##1|=k95pJX?)nJoimQ z)+CBB8*J6CFRNXLaO7Jyq$Hz>0P0`t|F9!XY_5=U| z>pc7>rEd&6ysz?0X+MtLTt~88oy+4fI#ya%ZD=cA>C%mhYSL$hTzbupakbMb9KMHD zfgN&9{xCj~Rw$(}JE$$}M8JI^y(qpJMZx6%Ps&lvHlCZms`B0Hj4`KydV7HMd`f*OsnV( zXbTinbUcgnyM{2lT&LpoeLMjW7T@6kEl`shZ%xZ+N*pX7;McpP&%#wy7Gb{5aEtmp!ocp-nxws}-qScNC2{b+l0`!VnIPG&UTrSGR?Ai0}E^o zjeFy$NbioO`0p5(q#f|qrSf3szs=7WJP7RUNGf{-f)hJwxvkVu7mS}S`thsBPrn|~ z;MxrW1L`4`n^rDBE*pMx-!@2V(VoNbfzgeYm_Fl|xJ0@c8F-r1>iSo^EXo%Dd2@@N;$QT;z zzXXdtZ-W(MP5e>A6E4HMo^>hHAbV*peG844z`6h2vrTkdOO_vD-z4g2V~5OlF*HWG zueSbI>MIe!FzoHe)tyN)n8y|xFi{vnQzT<(L>hN;=#A_Oq((hL>}ZYYb%9%JaYW%6q8z~(qgjpF^1=i%lHL>ympbd*vD*7u z5j%ncQ8-op zWw_IAuE!vbgZQ-8gmn20X}Qd@2nACnrF$rMjU=237(*Z<1lrfdlLxS0ro5oJM^0BO zL|{)bVIQg;H{v3I@6T!dOsh|CCCXA5Zh^HP)}X+RqLVz^(u0moyR5ZABhG~Tu+96C zKwYokPIR3e#MqOKGzc^C7*&c2>5SUM?kxgSae7nmi;NeUhwLXR3$pQ8305*q0Z0YW;22bnKK>9tmT0@XT* zn#bxz>pWO2&D96L7=~tgnPo8}GYK#q&RwcwLzUMk6ouc@8q(`zPMy=2I?wR!rt_8U zYAVtfree4+l!y$C&74N`1^7i*E8H#VN-SMTtdm&5OsY@fR6ks2!(E9UvIJ+q@A;4t zF-Pn?@p8MTvwaCPMCbS!98~$b_bc`u^L7chx7>#N`DA?OaML?mdAJtpt3ZGLteoI2 zT9fy~8T68682m6cu@w+LjK|j2e2O(^W@2mb!(oYaGOwc$aFSz9E-qi>?zR~jRfuA9 z6ly_XW9t`mG98_`I6~3hubhfVCrPB743 zliDPr?WtF@y~HN=34~yiv#K)EjC@v22?o!9y4W*8f zll&g%-0$ak2l*&N0%sb86~7P~6Glu@&m~Y5B?qZl2#^ukq#al96DB^ZU@rL^>A%}x!v8V( zfacD#lxZR#^_b=I&AOnHCiIp%M)dG~7vvAGbXl^DsHG!8{x|TyvD7gfdmjqAFC!lff9%2mxt7jW55k z)KT0_>E<_}rlIY|(uuj_q7+k~OjQrS8MVvt0%syIulY z9c@B{-vfrs24d^mKPS(Tr#iOYL(NZ_4di(CwI8bw=ZH}4^$E?M!uoa$r=&~x(5aV$~AU&rl zTUU}Q75QiaX%*o97)h&duWdhkaN-(z`F{J?d$yjBX9rrVSuYrEZ*DsrO=~~g-Yr#} zZEJ_2_>5;vd+nj*|D-ZWqe9Z$$?Bd{zlDxlj6QXtF^&~^kGi#!{I#>mn17$@SYg)> zGReF|cLBFX6tKIIuib6Og{N9Bw()5`((9EHfuYN^YyYBto%vg()+lLPR zUjFv@e#qZ!;%BQxXbI0!%b~dUL2kWTp95{1?R{H$Y`tU=TZ|#$hxdb#Cf9vi{@5jZr-#;$Co}0cn5LA=9{!pBSv&($e zZni}1OK?2jlo&jm$h*y-H16Qlbe+d+*Pk~${I@@ zC!7)_c&b(cM0_L7J>7(ts8sMzq~fcjKF_6*ibtu6y{9j7578e|wX2BG;YK>*t_A^M z4vc}t!-p;!%e(9XTUc3^<&B4ur;%W6?}!`swT!8ej9}QZ;+aFqlO^NNge(zyWC!dv z2xojy%k^6M%!?*>;JOd0iZFC}sS>6=? zS!_v0(0!wL{kYPr$0^qz4&QoKB6hmDfZioK?-&3_UT2t}X33{dsLlMOUWJd@=<1Y? zlRIB>oJW#ZcewA6C?zw+2+@~iR&WQO;m%@F=SiWEptXa9q>hDp2i&>Nj)mPF@%f-6 zEDe9Rr0cY*Sk!Ss zxK!YfC~5%EVKM5c=uW^fM)PowJOI&=rSibd_{i5F1^j;oDNMlvy%Y7~M$4t-HfGU5 zwtm6N;L83l`E>7u`82`3ibQU{Kogh&@=7J5MClvSa7-o}_4KV24o<3}rF zWB(6(Zvqfy750n2!=j)hqoN|3j*5y2IKTii!!pCBpr9xymx zp7WeD=Ui7FhN&E;L&{n#Ea9%aDxV|DTBAMIpq@O@TivfIE*m#7$^15;#1VGv$E81D z!F24{^@~hH8(9T3l0bn`g0W*S)}R#A23~lsF}bt893ph;cFn%$XtPOs1N;>M9bE?KH=WHR!f&rOWX$ z+(0c7pOOthAK>`PIjUED%WlwJg^CCvyl+$ZwiF&7HvJ{+`+Qh))QfqGBF4ewNekah zabNSx#RxIWNQmQH(tK6d@VP?TFQw4evVOcG!of6_Un`G;XC^kfw~r4yc5HQ3aRmhl zF?DPz$ef2FlLX_r?&j#GITEKGl~av!l9cF;biU}!E)t3>z5vzvHJsXEGrD-P&4O%b zx^K?DIUjkpSZxWz*^&kasH|`a^K|4D7yYtMNo16esSEvSM}KfUk+ms%lDNLzqz+K} zw6AY9$)?^4yI@D+X2#eA{w8sL0mw9@co;T?iE-QQ!_LOGa~?jAgKxE^kLz*$OXM7{L!FI7DaR)0UkJY52*t+5T3*ekd@7a^>#IN(Yg4sl{9dxWt zbr-Q5g-!oh>HQrhS`4tmT7x$x3;bdQ-JI=!T}x;&#ovz;mf6s3Y966u!wcQml+Y33 z*k+qaw)I_bqBfllGoc(OY}-t-s&BO(XM*DvrU~VkG0&;Bvc}f5V%(GVFJuaVN*0FU z_T1Pf2D`d@#@5$6>6X+KgwaO@F4^bb)F!1=|s7e!0*U@1?CA z%@=R&OVRa_ja;(3GpBlQOYXEDW^RvWTy&=Nw%41nruJf2D8g@Cp`4XlInJ$SSrj|Y z#o5{P#ly;C*)5xk9JIL*Cwd+9IgZ6E;Yf>`?3LV#?;{peQGK)bb685dIJ%+cm)XD5 zZ|nVzP%e7xKV+$d;M!?dC7Mt98Ty&(NRU3#ghQ#kmm!kK!1x zdl3z2UDyXB8d6nvFXAo^dT21((TY+LOjSSO$P(&rtLMykgl@BehM4q?cCNSed2+FO z2<}tDN`siclNUAg=e=^Vu|4E3!Btw4I^Rotz1{M`*%uSBkF@Eq1=86!T?A=fKtOY$ zkBxcd4sV3dkvO08VQeWsVIMM8ihZ%*FTkAyF*vJQY@|cA_Pq%Q`$U`qZ-x6iVaL|m z(m9bfR%e_MENH5*W z8uwZ996NziRcGed2pp)<%q%Nn;SfDL=v;YLpK@^OW?V#bth~2hgnhEu-xB2@rt?o* zBinN#9qjJyr~R|M+2-DUv0%2kw;y|0z_(9C)`XVKt|}p2J%-3w%`P(yprCfv^}Q&+ zqB*sXaeO!S2zM}B|7=@ck3lC+8G1yqpl5@6T*CC?6udX@_I*8$mg0l|h!fiD+nO!) zIB%aeC_@%b(24rk&Aw9mM8tVfeb^^T=FE_AMt6!JS#kwke+><-Y7)h~Ycl@gj+Qye z$#Al(v93wP&-y6d5DDJ{TP+BY(C31}Tx<*j_X*vLLy`qA6w5{Hi;1yy@1Mu^LTppU z{lXmG{z(H}ip>tU5VV@p?=hL^i1U7bkjaKj_zABao#{WP-z_qUaI|^9%f+HEpZto> zyw;jdzVoF|OKyqMDeV^bpdyd46gR5|pVc#eB$&OdD{+C{zi$&0F z&Z%upadf5NG`uvq^y*ySxwuuI{p@&pduD{paYzBTeJIqp3C2@O-(a0R_6b|2m^y@l%(tz8!SaZn=|_t(#j^0B&AK; z3(yhaq;`s}uVWDga}ypcTtNPCzXZRn?Xl%oel||&!gvvtY-)|MLMMFW84I@-!%pMO z3787UwEOY_q>|p}D0TyyY$jRP*L_K1B3t|W5!n3Dnlsrzqhy0|&j~ih!q}P*$onfU zOYgHEE0QK3aXT@Z&}>??!qIwtNlkB| zo~QXRL_yaRClp5pT4UQ|h?C>av90EmI$M`vXL~m~wN8g0aOmxeBkioe33#Y)6FR5V zE#S1ijz7pz*hpcH6!A@ZoGIB9!e#1w)m+*dW(f=2itEgS?^t~4pO_v&6!3~S{w|Zwo$`MQrhR&_#ws2IEsay#Qj=RTH0vM-$abQ8Xs~xbRxcq(^_!T&L4QO3eKLlosDWRcqAU{<%s?JyaR8`&fi0SVAUyB>y z%LzW9g};B@x=PG_;0KR$9{w5#1f+@|AbUDoYL+p@7Y zT+qlGxvd2*>2|15kc-X03jSj7isA!(F=u`fP;p1!wzzEU5*$uu<9esugBHeL;p%^c ze~a`#+<&^Zycmm*l>Sw7`fP!eGy3Dj!COA(8cpBtIJ(=o9>HEBx{`*&PrYv1U^i5v zu!qWcT2K&2aeOcdCZ z>B%fQ(hPNh-+9)%n_U^dn(@!!Ffj6Pe?R#)?W!5?>P&RRQbCLK6U>XejxYa-$g$ZZ zKi_f3Xa_7fU$twCm8INscN}%+ImgcDcVnH9>vIxYUQgE!eCTuPSI0cZbjMq#(jARh z3p5@}cRYKvPld@Dc%)B-VQ(c9hXAUguQxVnY*f_yc z_z7+Plf z84>J4Z9eIP#lTyo$PZ`E@0VacIeit0Kj`zj$;s@h#tGPn<1CJMG~RUFbJlu){nscw zHvAlUa%VHv>h9Ofxg~ln!r@QY4vXZijkFsN=cPSDLXG{4S$6s){EiE6awV<5Hc#wP`N3dPt9#9R2JRD}MDC9QTx2@5g%HIVhRLjuB;OZLcBzxZ-Lg zfGF+m@5cuo0(z|tes`ffW*m!)a7QwxslF|Cik6psFCNpjy1320CyyV`#3KBCpJUby z)(_|0eln-wiO)%&Lv!}q;3F#%%{KMwOJF=rfYYF{Jp_%+bO77;v6#Iu^a<1kp^ud1 zMnC8oeo}sK>_7LK@hV0>V08lPELg3;I$6JXViNCtVd!H7M$kVQJR*3ix`31;H@lmq zIA(@7&Sx*K#8=noSOkF|Nq011Y1#y@^}w@b7+W|)gX4&}Y3me!DR@VW zf2=F&_c`tR_}pDc{2yYV7!PKI*)#zG#eZ!F>Y zH*d_~?c9x4m&m>H#&n*4?Zyk>Q|&DbbtoRX$ETuBe-(_^N+54ow6N^beFQDteOxTv z=I)I)3~~iYL7|{gpkz=s zXd38M&8s7e7+X=~{(w5nVAlFh2<|sks3iLpb+QYJtV0&E4W(!=Aqr z`nGMxrM7O5`s_%ZxM*M5@TkvMYIk~{KIM`>yv*fCs|mw*hI@XtdE~O4r6nQU&n{|C z*-g!j&Yhf_m!B)q7Nlx*#;m+t#Avss$vs7rCedVP=cQ_xW4wi1iG|tCE$Xy9apLAzDPs}bDEu1%l5k4UXfBu2a4B3ir-jYP<>7A@cv&!K!6h4ir}1v7oZie$ z<@plM0Dda(FApJDI0+ZVL6v}Tq3}gMVi0EP?D?7mt<>mGmMxGY(y($nys4wOXEKwhz%7$7r%m z&3JJFk7P&wqH?p0S(@yuY1%M7OsSgeIBhC5rHBIVZbS$o4&m%TP9O=$9V7=ug5p8r zL75;UXgX*#E)q<{p>OeO^4WPTA2cSnF zZb%4c1F{1-fW#mtkTa+|NCI*Nxq;k4QjiQJ2L*!Epm0zmC>j(CiU%cwvOz{r5oj4` z9cUM*8uS3<9Eo^9iJS>ymqQc}9cU=++#QP+8Tvp0$-Td*W0X*DTX|C~4e~_(J>cgB;J|?}Lydy}9E@B)-t7f$5U?)@(QttX5AR$s&>iSDfa7KY zCxS>H(+`2B!|(#!2ZB66gFx5{jl2XQFF{;Cn5Tm#f|94E8#O7}+WrWeJT=9_Ow#Jl zB^wKM+Prjf=8~!V&6XI9=z~)w877TBjqyn^xf#qnmFf!;0MA512RE6CpNRz?o4dk2 zO`EPUWg8`_dAUL`NosbU!K7#TqB_;+pB>&Dzh&?&Ba<&>sww@(*e#tK-C5S39!v7n zEWOc$(x+t^blI9zZH_kAD9O?2sC?Q?$vcEL?!)D~nJEt<991AZDC#xs*>^z)CIp)&qOW`7fJv-j1Qsn=o< zWqxgi{zuZgw*=8P_D5(5Nt%337P>i35|4LZ60S9*>a%q0Rpun2^m>P7YYYYp7m3Cw zackxlLgN|8$m}&T3$LR-HM7tn5M0SUFGokcrDR0PB=pjfXd2@2-3Hf+!+(6h#hi63 zu9cP8CL$ujmL6Xz7fXTNB=XqA)^?n~RGJXm8j(rt@Rzeo&P>L`aRs^pJa(g^V$PDn2FM(ZwC1#T{PK4%=R#|on$~^Mw$6> zW=sH5{9{3sHhmi@o*a+_qyu#a(Z`4KS_GoJ&Nk!AK;q8h2`lZ@UF+| z^HQ}20~mNl6sUQeX~w+^)Z7h_wm+i}KT&gkg8F`~=KMhPY=qy6_te~7(9dtExv)3Y zT=_KSfAcoxhx}0-^n7EEdpnSLQ-R~n=FAZoe}b-oB(WH$f+|2xBQf>^y$(7KvKxi5 z7-$)&0VI#ZI2N=Lv=vkevWx%cxk4I7@J5W-)&^)MV*5ZY@n$J)EXX44j9Lj(=DTgE>%b2C+T>f+X zI=q>bQ!U~$Af8Nqj;28yq`6zu+>FZoPkE*AG>6UQGMa^@-28LcXV2^YO;`h;ZaT^$ zc~5GpXy5dm${fE+)f&O1prU8f&8znx!g)epcF)mhi@VKwd_CA80PGN4hLG zX(&nlzs?WS6qtDF8!sl zDfnCPl>Gghcq~guZRW3Uho$_}xT)sWBh2ZaHqWP=3jOA@>zry+2Z@lzgtP=6kQmA4 zX}t@3(}j8w(r(@!&}X*LC(w6V45FciMz4+IXDgWZi_CmWJ%a_0*E3ky8}$qpyhc3( zedj5Sztc0&w}t#fhs9`gjk#do9G*|pEA4Pijy66oIxH$2UitArh)x%qmz|Ybh#Z(d zJn>q6PF60j3yaYjGxO5OZ`AO?Tvl#6H#}rGm#fJobc@3u`ewuKX22gZ6F+KrI2n0< zR&E+^)((_PrQjVOgZVq?45%7(0aOdR2C4%!fF6Lj1n4I~&L9cM9V7+GKypwZNDT@H zMS`M1v7mTRB4|7)4Wt7VfXIIlXg+8WXc=f7Xd`GB=m4k!bR1L-x(TAXpyvV1Tq3?- zATh`pBmudBq#!jY7L*J!g64ykfi{A+f%bq7fR2OCfG&Wpfo_8Cf*yfv#-hBS?jR{B z5JdhXf$^YZkPb8#v;?#svHx%_hSewGdX!Kl&P7f^Ph9b?O zrfe?6l)=Smb=(LjGlu6)<-)b8YI9Pwdhifu9+?oYz@qpY%^@+^VgibZ8I+bbsF05*4UfAd29Jacg?PBgNM%lr zl8vz9`2GN4&_|G7I39M&NCN&+!;i-o(gYmL_qE6m-+K{KKSHAwJ{fcPCOeDA(CCB3 zN4m-;9*di_qD|pgI!$(#9`*E1yrD>y@;Vr?r2u1KC;6a-@JM#b)lhTU2ua2vzzkp< z!s&n`_-_Q>S(JS$?BU?20$KbfK30l{r9(X@r6ZJ=%%qnU(xtJJkS1T1eEQjD|153l zU1|0wT8xMBSKiSgsnrvCepK>p4DxnaJ{{x#=6-4=OlVGjCl{t4kF|IasoqS-IcnfuDC^X4yj4M)bl@#dn%Z@sEC%!pZS#|34nQy;4TU~SR{Dq5`F4tbU`u(*ZuK!qffFV-t4p`;&-dsl>D9ZBYu|qT z-3Gic(A~pxkkrfDN9OD2FIOl70)v88YE23j3eqz&vnEZ>&dJTwP0<^Srm6V_h1153 z$6l@EXS=^Nea6gL|JCLHuTK9zEdP+uu<(e%Ln5Pw4vUT%J_5hTjEf&VCLwX`i%kB{ zcK`o{{J9prAN6uV{|7DSzxel`&;yEQH~zy$r+@VbJs9E3qNaaVqJQ^K2ya=!e^*}1 z5M;J{O8?&hhq8(>gBMb9;+$Y+TrtdoiQNBHaT`6g^#AWl+vH2kHJO z%d$dvU1kZ8=5gzRq_5lxv;~#|X`WdDq`6=vko1k!Kzm>t61_lG2fhwQ`s0O+L zLx56X7*Gxj2daUCfsw!=z*t}uFcCNum<${S%mmUHPzM|VECAB@un0H?I3Gx3!9~Cq zfy;n2VlDyB0XUy8()UA;9jyFrXVS9H<6H0Hc9}fr-E&z%*bKPzM|e zoDLiYoDYl!E&&b)mHTunll7&;hsz=mJ~`3;=EeiqKIW0NMj9fo*^n zfG)r~U;vQQ!=D803TO}P4r~K-0|o$P&a7JJ1Oz3WU7K4s<8G8uB7L zFq|+P`6P@)J_)0dPr_Kdr$C$Ocu#>Yz(qi&+2EiPAk75_je~|qVXhs?&&r9<=IW#+ zp@;On^pMt{7*pV5>n`zY}jQ_q;oAAIFoW9M}F321KGg}cD!s}?LJ$T^j zPTzBC6J#C*Gqnp3aM4U*Ho+VTO%Q7z#HX~VjgUDWW@;B6`1+IoT+|wwX$(N^!vmuL z;!_);JX8GCPCQ^DKh##pOz~5Dp|r>kwHYce<)7M(2TD%y7$6biQ(36}cpwLOtzf3G z@qAcnN0cswrM5)*#4CuDD9+Jl8B!TT`89oNUlb?Ffz}yGo)kZ|GY_On;}>dcWTx^` zd!slhpVa0&;Dg3K)b1#sq?x0(N9GstHySphxvr@#3iU}X6z z?VQZiuBfe(nc}DRPI7Bm7Ha=O-KFsPXX(=WAmoAG3BgS7MU$DeG0G>s9|AvzuL~jn zRFBMTun0@<4dtK0rt*0nf|}&;R!}{%cY(c2LTD-vy;D@nRBGYfqFAVQIkYfV%1~a4 z)Q(9;Ozt_nOo#I_V((!THV)1!L0;6yQy6k-9)`V%EYG2QJqWU(K8L+$L;3tOKk?=^N2y~j(C8CWPW1J?<>U}bC2v9O^Q{$nlpBh7V5@vvTlr5nfhD9jvb zt{3t%-28nZb1a|#rtuuTP3&DEq`5H3kmm62z+KZq_>mLoOz1fPdo{2Ucn)|0_!+Pc zSOUBYd=tpM6vCYZH~_x|Is;DuU4fqfrNG_5K;QvjB=7<-9!NTXWZ-dNHgF%X09XMe zox=d&T-e_Ot^`g7k}jhca2xEYWJi8Qz*5-h`+FQX16U2*2_&756|ezzl0E5sY=Dno zr*Dwm^bl?ykaR${KquJgTTi+m(hW&qUkh{xE(Xeh^xlR8zW~MpHvmb;K$05|dohr7 zNUed{u#*mhbV+u=0@yzQ&IO(TE&^@@t^^(dt_Qva+y?v-SPDD{JPzCjtOkAuyaucU zHUR0{{s>qKw3`vaod%N5${y$hdlgUuya;p$o(0N*M}gtM?Z8-I8E`!C3@{V84rl~! z0u});178Qe4O|AS1+D{r1>6e!2Dk@!1y}(*4?F|B1gr(t0B-^h0Z9kd2lxPXEs%6e zq<)W0&<^tU1iHa)0Fq9wEl>vg5}+FRAut-a8JGy9 zJ|_*h1E>S;0nSG}q^p_^`+Okjp8SaqJL!x#gUWe!iybJp~KyDUJ zZv#l+PYiT`o%$yZ`SAca!>$KPVD|#L!d?K(gk1@g!u~SQ4dL4ZNk^sv27~n$6uuFlvU|$710L%wg0!_dRz|lY{;_m>ggMBIRE-)U*6@_r`0_1S-2y}pb zAutm0`T?C`p9xgM-3jOl`xtVEy%SIhdjc>JxQzI~*MW(^)xb>PdSER4cLo-~o=EPn zcLC0Yokn}4iwp!Vg8dcXO5n%9^}wmXZNQblOoZzSEQNg>xx?NYcpUaLU^Q?runt%b zd;r`Ew42QiD|H4QgWU~y7^p*d7oZ&W@j#l}e-8|YeLrwK@N1wE_!01R;BsIA!gT|# zgM9^XD{ul(igNe>_rN|4NIFzCumbkifQhinfz_}V0h3`@0I$J58(4&Ry8|0wp9p*e z%mvN||9PO@%lLf|P>pcDKquIz14&mc0ZL#`0WO079zb{4lYw&hml4ALJ}?&e60j2f z`U5jz&jK2OdB7r|HE_&$l5(|9>CvzC8Hm^q98o-*?!K2OX%nQ!0BoXwY2Fc&taMPnu*-8}w%Vt%Gr z@M*Rp_@SAHU}m#G=D*OKXPR5FQGBY!_?>as-)y)G^GDK&cpzsKC({EkEfJf4(Y{pT z(-zEz=vM@!xYc7N+pTr*zrg7iOlv0-t{2 z*<_}^f$1*jH;D9x(U^?pW*K}x$i`tBz7JvM6h0r!%(PvCnexQUEPu?L#iz~8llVGd z=3I+-bbLEte5R>kX4;P<#L3za<4@!3o{ih6hJ?E)3dsr>hk`Juh7P5dVRw5wI% zv%N*kKa)E%XYev*=1jiMm^p|4E-*9wepZNwX_y#aXD&O{G28nj@TpBOGtCeMvx#pj zj6c;JmgbmRvzg}SqneK`0t75tgflu!_wQ2Mxh}nbxOJ>SH(_k|Fi-vCxOz*=qiKN%` zfHbL$Y!1%aF*DO&rt9>;*Pr?qrd!G2<-_zNllgYV^b{;Vf_{P7C=BVeCi7)xdX&k0 zf5Y@fOjF79NlbgmbPBA!GCdH}PBQ%miB!`Cs>_t0NK z563jMOxM8bjG5^#`Jq{v;D=fSo>pUmQQA;zcgPW-4)X=GaVl7kZ1Ev>iYyeHO)WC z&O$MF(gj$$likvtB*Ri?{z*DbcG5@EE^%@b+9malq+=p(bG~IC%={bB&YtF<@tgZ6 z9i$)!G7DuV{T%HQr?i-Ei+yQGmq?{$a%8`n5X_`6WmVxS^ zX$K_f0I8fhq$ubEnI_4y%#6eST9$$7NT)*H7~9}o71|k_|I)iiZ>F%9knGfgo9q;h z`OW0hXJ*>rF7OTfyT=ck4&ht2N%_8j*_t2EHqKMury7cQm zg2tn2t9M$LEg0k4{o~&cX5>^DF63;g*NZ4X**9tZ*QOqIUU#eSOI0Uyw=Pfa8T`|# zBpL)^c$WLP{KO=OJ?Y*(6`s~s5ijTh2A zwe2?t=Jsz}KfQcO-%U{+5L)eW@m&1sK_f(DAvh$At3*Ss$ z@&cbmFNfE@>ay;QEn_}eJ*1W2=fft{CS53M^XcOTb=D~ntv_en)b<wo=Wv9Z(G(&s*U{$_T~3*yt)5uf;#9^*74ae zi-UgYADg;-%8`#c-x&Iao2Oluyxb_4+^!^TwRb=DCYxA&kB8q@EgA6DiE-&mJp6p;bDfGgO35xHQ zcOCnnyZkNgJEt#R-JSp8G|l6k9VTfotGw3!`GpzfV@j`x&~BO~{Iv-XFGQL|`7F8-=u$$hI*W4G722@P9Kht7ZEw7cf& zWxb;|dYD^&=h}NY`xl3eJbC1u{-ZZs{jUG@^(#O6EQkcD9$I z#=!z>^yNvx9VI_Navlyk5*1u z*74feMN0LEtEvqT^YUZ*UEk7%TAA2u^alG=i4Gqf>!%qrZ%+3~Wq0S9rq+eKpBz#W z^1iLzk>FNemef_8{Bd;Y?^iw#7v0)AQ{rmqtFM~?>4u7>FNA{s2-a=dJd7uH!ZFD=J(gmpSU#q)zZw-YbIoU zz96uJ>y!u_OwNUL??1FlZo=l2o1ZS|ee2>MYj<3`b*0OluhV^s=5O;4cHgyc@uiJj z?b7a^89wQWZ@WojGMsjwS04>{p&;`;@5wa>xBRvzE3F^ObM03VR9~;)!d1}LR_Vd@@ zH;Q{IAGMGCVbdF_W5qund$%s@Y`uJLl-x%VN|1fvOkLtFa z4?K9wLEMmZr+s#L-_YWRM^-IvYuGaViR~*MVOz6I13ld5CVV8m)w7aQPdofW%C5Re z^Q*-_EIUzgaP$K4=pAD#&+Ppp(d*@1)?Fi_trbUNx^CnS_PkmeUAU&`NI;dxXEz@7 z&Y8Gkf&8WFu7%&|ryeR;z59p4;nqv8@m&YbD8F%Le0JW9Yw!J76Yy|NUP}H*87|Hp zJMdECrNEPkPX_nyG(BPcSksUF;xh8Pr0%?4pVa2h@3RzjKE5Tk>*wwEx)L^NQT4~)9hmfvO73{?i^UQr|AMN^p;cv0eJ3SrwM}BX-eyD`RhO zx^UQiQ;^(0=8&N>Bj=}9>)#vs;EwfywO2-!DEo-deqwkf{L7jSC#{|5>E2Ub7*#jw z^B;!)(WUjEk7D*-IlH0Fh@Vqlf9dA#Z+fZI&v0`(t+{Uc{`j2%gXYer>|g7WpZ3RZ zr>6u>Ex9qsX-0bVT_4ZeyMBG+ImeD!OQH?y9s4D#JBk9^%%5_+m&b?xw~m~9ZJXCu zp&`)^zst5QuGl5Ay*mBpcSjx2l&5@JeQw^RpL}*{G8>kBz2bPsN#B+&^xzwexMQ8) zv_tO?=zO=0D(cshAAOkrL7% zUeTW3CprdiTl`q(S2O;rSKf6B{nO?6;0G7w*H6uBoz@Lsy#jg8a z_7PQox?erJCjWtZ(Ag_fmJRyxTl>_J`iouHZyEgJ3*UWn!Rm#_7YBc{(6wX#O{efq}{lJNx^VD~k=MP7#+1LYK5>vNw1-Wx(+~R|cYO53*0C@( z<4?bD)2GH>*`3k~a?@35Sfw&y?>9%}uRI zI6iK>?~TVDvo#|RxSap;o$afJ-(C~@>PWt(-ZlRy{bY)_plEoIY!9=gV2!2Xofgwqny} z6lc?R0(LBAakdU7&i1*NIoo!NI9tcnTr0=TTr1qq*{c0Xu66rbu62hyTx zBD+o=BD>CEBKyu`MfP2EB75igqBhPYqBdQ3i`uxH7Paa2i>Pfkd#kqH`&zYqp85~c zv(@2?C5GatQY$`y%W zG54WGq{z(9%kZM`&7THRwc=?|`B2A*M?hw4`b(!UKjkS3gBF?A<3>a4>1yGDj-I5$ zqy^-iO3$;~&R~Mb-9AvJ( z?-%BXARAalM zOzZOUIgKHUrYxK$HIJXz7^djigoY0y-R_U{klWIUM3;7~N+?#@ zO8$c2$F zbc1{9q5Hpopi=!d`*w<-9o%n??a}39)%p07DwnTuyDoRwYEkf~s#i}}RwSIlNlNN+ z4%8*?cKJjrp9h@EB+Ps=wZf8bCawQKKfK3}SQ6EbAF+kOcDar};VzET|?KGk(%y8%I5 z?G3AbKkZQ+JLdWFfU5dHt|Yr`Tj*ZZjhKbMI{ttYnUW`cX1j2o>f`*KYd?GgCpJj} z55AXEs*3pi$}bCNdy_mj20vG(+T1YbSj1~Rg1Aa!C)KVp)tM)6-|pI01p5fv$0PTv zO7q>+_8T7ua?DohUi1IuWlY&f!Om`8=SMp8zT2+N= zb)R{!mL0;)2@PW$+8wV@T`oDfX506WSLKhlE{{E;`l0s8G%JZK@^flP&l^Wn64yJ~ z?R?Na)Yir8a*wKJjT^XoW4#Z;k2<)MJEk&xniidP7wPF9PQO0?m`Yz#(7VqQ$V=_{ z<@c?QtEPWF`b4K=B){x4s}>(u={H_qGI&S3AZ}4d%^&SgsM;>wy!gD#C5RLMxnb<; z6DmXY**$$;LjICxjtc7hjq2Llo_DfVc_IH+7u;8TqndYn_M+eF;l60e)Wn`ARotR3 zKhyl|PdQYn@-CAu9C`!gU4QCC&67%1(W=|`+WDfr7wq3V zNL{5W{<3$dBoO&ol-2d!X;rG_<2`y6-a-3M+$f#4y-GE6?vs?@laR+QWvBBERjSMF z9Qt@B_7CDBLr1jhcS`lJ=Q=m>%ua}}{YNP&r&JxfI(WKl?GVIOhm}rQc1l&~|Nh1- z1x^bqYCE;;+f%B2c8+^5{iH;D`0C>?98Rmet9Nu={ubWnr}gg^-v_>L7-{}qn6aVm z9N)7ESX;#G0+#k)Z1yMU5Us(Jqc){bU&0o|UQ<=q8L-ooqx zE=yu|0V^GuUBII9@A&WnuAj#20#*yMURKnO(pK?#wP=!EdMe@B$Wn$?O7hbC_Mg2g8|Nz=AI3@MhF~e~OjIjJ2OL zyMWa*nO(rh;mj`JuIHIuz!J1iY9|8DFK2cEbxWCDz{D(O7jTz~*##`=!0ZBYH!At~ z1+3oB>;lS{GrNFd1G5XLi(+;GOZzdqfXg^$7qE64`U5-y7UeO!fRY!OUBJ?7-|+qg zOkU0G0&*`hyMT+jF}r}$?@sXH1uT7+*#(Rp%j^Qyx-h$d$=@I6!wcB3j@bp&ZjDYG{sSF3V@Jm@yV=Ye#8A8D}Y!b4gkrYjPA0v9b~YwXK_yV%#@<%Cf>DhVw;g9K>6n@N=lb4oGVY1WMP%hdreONR$?PuH8>3#D9Ap%*BZOTwTo1#U^X+5)2{9VZ`>PvL2p)qIS|6$vhFuscZu zU3MmR!Q?_yEx{_B6gy$OL4;E5k_m&>TF2|_v!JskO}87?4F>ePB(}*;b*b6fVd|KaP{+pjk z{kjdmcZXI*HOP?`;V2GPj%;%|0+D;cOluKz9#gP6B%NzOD?6#+lN+5OApwWhjWc1Q zbvOE(i&D^<39ZR_@*;a$I3ackUn*K3lptgt;*ucd0zNjQIs9P6$MQlc{@uC5G((Hw zL-pljbVJVcd`bIa$H)M@AvE`VT6}b3v6e537HLr3Qms(EkX-3Z238g-(9^RMnxsc-#oET2mGCt|t&~DY@Q<|; zk~fty4=X;$^RzgG)=7DxR;DqR`DuQeS* zmPz3x*e5y~dp^VemE5WQ8~AojXJL{2o8CX6|DYAvmVH?Y!ldILrAW1dQ*Y_iU1MIJ zBs(uRgUjfb7k1UDe#94ToU-G#?=aGb<hJ94vlN-WI(vqrm8 z>_J`2_PNG~gftJ%vfG^ZAGBSlz~0!BrgSKYYp~xm^T=Iz==)7(HOMjy@k-&Z`nPGqqv-pV2`myT&P0s0PJU4 z#7F)p&-7U4%`z;x$>Gli^&>o#2PfFOn|b6eJeKiTh9UP@_=~oPhw?QZ_H2-)%*fx< z@?{y8;wyl^l@{?)n2oUS0txabKTngHWjKoK#TPIh1?jMvu_y|w@I$fpM1i{>Kqo-I zfkwrJ#}&VSeOQ~>gF=h@E-x6J7~w{7hbzY?=ry?>RRx^z%znJ{g5Z0mndRkJFq#wjK`-j zw;3;%Ph&oKc8y_UX-hA+sJXvH^0=fqkB@IzGae3K#j~zRltAK#0;w+;3Pe)e zC?K`{7lAa7O#z~4+$12S1LHFv@=ty!9?8eg&I{#ZJL03~Yar$0pxJ)GMNMoj65%Od z@jxAj=GOFmpohW}(tGf0S2Y30K=cS{%ht2B!+}D1$({OkdJw#^ zymNc28{@T1x4*lZn+T#uNVn1=UE;S7R}*j*VQTIaNbt9=g+I4YHF?940cvhKh}n1+ zcQjPpm>(fu%!RE9ieRZkIj9{_{nf%uyau4~$h@PkAoA}w; zYJx`hmgz4w{k?m{Ke(%3Y98J)e}kv1xuGC>g#1~`o%lOnQxi10TZSjTU$L5?(S2JB z`Bk@YZ&^P7H=BoV8ULlFfA4^SMhECwPF2oBn>GimRM!1L|MHEq>)8wW_ z^5yj;4ShMh2NNZ^vK|L#=kt=7@a&ZE{)?9rG#MCkRTqE4|HrO7gABbqK|vlrn+ z-@G(!Y+hC_DVxQx(-j^qZr0)!TSplZ^78#;LMFLk5u=7jMEiJqrqLZuQ5+R&a8@=V z&oUU1)6QHZrZ3_A2v^0s;>_-xyxds5Ha)8#fs2d88Pc&)eDKwkkL`$-uRVYSl(z{nj_p1hP$r8iPsn= z$57N-Eti+AHD`MkN)@h6F=b?E^~je2lE%qeBTPn}$;k6rZAgdL=jYhssPShyziu0EJ`h~nDC<{|PqQUYKNxbtZAwCZ zE2)m-j2c{Nq>IW;&r9G6h!c~SX3Ew^@>xLsV{As6wEDtWtsWQN;NF>3ZCIX(uW{$U zw!o+~uDb;{p1*AdOzsAsRv~7iIq_LJ+7JU(IGMOqi*^|ojTR7z3u?I2HlvK$qjU3d zDmmv(rM75`9+0SI%S*C|p@o=3a?|2;S-E_Ux%X}38}E`UAu|fg?u=VQw#HN3SCfse z?+qe?=&Tf6TT{qwrZSK)L(gGO9)YIGO~f16bTLRig|L(-@5k80 zB`KKS=Tp3djA=~pm~XdAz#Vm3^1*#4LOk>mpi-ig+mDP_JsaMKSroDUm^JM!=wUfw)P%VIT&2BSK{a3z z&cd0h6p}C3>pX8b)CR;#;EK`qAm6OCFzP07#}oHC3UP*Xq$GequF~Yd~i~ zrF&#yM^hlFQamIg5^_WV*N4&}ySXa`3%_jU%xMz-Q(nDIx&eCiVjN|r)G_(GP?dI5|hnwQ9n*g125@>W2 zuX&;vs+Qrr3*;rNrNe)^UHr_4B_(<0^PirT|B?uweK0o5&a)40{x8Yy|F%S**$l)i z$!9hL$^W)2{Z~Z%e|U2cS<%U9}S0skl0UR&$J{jj!f?YL*hKXVNqVx%p{jXXE; zoPD>KU;a`pJ|r)nG64C;Qv|ViJTG%dJ55B za{hlNL-Y@iRg1^k{Io2sAQypeS|*qnmWHx=@l=J3NztSw;S$xnR5k7|R;xKnx5hiX zpW=qCURkM0TAG@hU7^3Xh?m_S-rR$wJ6-3SgiE6GTBOP6hwkjeCbIk_7K@O8Wq9(} zGJnlW`Tv#8ieI~sV42PtNy>rEQxK&igR=bQ}7q(@L)74YV<#b-xoqB53t*m z$lVh=`4~0%r+8@xjUGt5g@)rV?b^aRD@?S{hwZSV`-D0o{%E*m@IL>#XApOOVSuu!p?A1h;4BrfIMbM;p|?P;2O~3VyClAa2ve4O!WR5=4+|2$V$g0+hZ(KLsSw;ATHiE7->pv zj;;4U#OBkR4~LKjZK?^HheER*9KCi5E*~>$(_-~mQ!!7?&>9*YEuA9@5Y&ibM{B2Q zvnAQ|H>khH5S2SMZ?ab3Ut-D%Nu?W8gZigyvJKk)l0l8}1P*$3DuIKV3?enVg`p&DjcYyau?;YNIyequVcsu#H_;~pQ`-J&q_)PP8-)F1OVV~nZ zmwf7d0%UQrwX)5!OR}G2U3`1^_Ve}d4e|~3jq;82P4Zpn`=0NQzW03p@ZIFM+i$O5 zs=`{?P1#H7rOZ~otK6ZiQeII$P`U?%2E+#B1WXTjC7?K9aloN~`vI!JQGuHRYXTjE ze1pP+5`t2L3_){)Rs?McIuLX$s4D3Dpr3u0aKUXQ$N zydAuqy?c50^LF?4^HzJudMA79ybHaTcyILn-20IC_ulusZG3wAyx_AJn-es}%a_=o$)_`m3%?Ejko zR{!1p`}`04ANRlP|EvE4|F-fEU_H*+K^Bd_m!7s~ihTq$$)vJC#`APhv{YUzb z@t@*9(|?}->;8-VHzKwDd>McAzwQ60zm>eTysvzaJV36NN66FVQ{^+|bL4NxKa-cs zPs(fLm*lpJ_6kp>uQF4)QMp~&A)rUV(ts}l_60Zv_798*j0+S6%Y#+H;lYu?(ZR97 z9|hM3Hw51eeh~a9m-CFQd(>v2ca(R6w-$Bzs`m=- z54_j- zfinXa2d)VGIB-YcvA}Nw>jG~D{t?(Q$T`S0Xh2XE1Oysj|7U)v|4}-Li67 zh3qum#Cq97Su5WJ-~GO${BHZH{fD8=ZTD{@A1NO#pCHeI-1o>&L*m!vx8?WcRth^s zXN61=qZp-_teCEtt$0(hO0iC{QE@B9F@|!5hJ$V;Je?_ulu427nAM$=uaZh2V zbW-+KhAD?CHK^mc$_40k%9RlTNdajAZw4I>dKlz{wlptzdGHCerlG1ARhv~u@mob2 zuVJs3*B4&9y>h&V`i%5hD|;aO(C=rz9{$7qm-|2VcS6g318-}&;;5ot;iMd(3{XZQ zm1)Xl%6-Z+%4^EoN|%6M=-DO*6a~B$usUE@z>$E{0lx*b3hWlxH!u?Y8&`rfkWP@- z6Q5vNge+DzPL?iPEPGG(q3j*MLw*nZLj3LJPVyMMt6I5K;fM6zQ+$dv8x;2yR!Xt5 zi}HD;T&YqnR~7}e4}LRvdvI0o8rARAUbbQm59lfll+KgBA$>=>Qo2KWPA@$1&ji;5UxK7RQ+-K!ETyxeu&)XIv_h(t7E2?%lf2*a{?vPy_qW~` zy<7P_=QF@Z>NCV=g3mIa)jqd;tYt%FV`Ygy`8cM;&}mG5QrzUkf7 zXO&ME*--T3I$5Rcy3F5qg0Bg^cx%6Ozjc1Q{U7?v(du58zl(m_TcJe@TdF8Q3;RJa zN?D*>quj3y3P=rjJzzsXM&P@FUj$wcv<~VWbUSDk`l#Q62dhe{thH>9*b;@ml54=P zfWa#9TF-xdTKhi2_aeYA)Gt)=5x#oc0;>YwR4qj)5#Q(kkM_J9akI*qgW+;qdyJHi*kpM<{+uUcK} z3u}&K`EcXYg{NyP%fHEa1rqXtR?)iJSo;ZYP`XZk7-{*l?lG=6ZZ@d#i{T;{|bxycI;;kDvLvdRx7Z8}MuOy}HXh4HwRvPl3##c{QHl&ae}{ zFZ^)$ymiSMS~qUcjp)V$8?}ww?2LWMKIJ^Om{DM5d5_@~b*1D~eagZd--dHs3)HFSb!K-}HzEIY;T7A5%t?!Jk-%DmRZ7jCY& z^4JmMdQwm68GT33>N!2Hmyk1My{GrlAf{*!b4+W^@S;EXjR2moV|3A+j*K4K(-i&5 zhv{jA5=jy?i3~}jGi1P9mgZ=l7I24`@bqO`p;fv^YqU-qbRSN(Xqz6;4(;M@KLVY7 zI-tjNNJn%`Cv-|@xR*{S{@oLEGY?y1Ugl$d7GOaZV(Uy}lu2g6jR=dfEf!;OmS9Pi zf+^c9!**Dfe7vxA>S% z*pyZ6n%%JX?WWzb+x7vnt7{+GJ(xbQkL{s7vd2j26MNNhJ054v@j5=o?*yEn6LQua z&7qETEN9b+I8kI>%!xY*C+Vb|^n!;NUbl)*|IL%m*c_hWA7XJg_wY6D=K&r>n_1@? zr(AN2Z}JF_qPN9(91SYTQ#{SLd4})sEYI;gFYqES@m*f#6<+0gyoTGM!S{I+ytVlO z@9-`^SUj^$N}l1efP;R8IcLNKOw8sL%q~bgLIuzYS9SY zqH%DR24h+9RRmiVa8(CWE%4L@OMP%O0z)(KvkG>+;3f!WD0qp0l{h#_gOM!wD1wa& zxTu4P7I^4_g+4eKfq~ib_VREqCK|*vDJB`g6yum+8q>>Saz#w7f{E2JtrjNL+H)IXrg5VITYr!CeV& zqi{9>U$^0E4xX0aXcc}o;AR_M9>U21d>q5YIp}tSY#+!Dfoch&qswg|1&(*XZ~^?5 z!EO!QHoB+8;HYNCNH zrj32#Q1q}-48>SXvB|ik8=Hks24qNTQljfeWlSc}+P2Z!ah~lw&!SbLm1CT~j_4P$8u$sWw$q#Z&^VZd>iBoGPf2Dyu5ex1pM%uJXmY!y4m>VjD^%V=mdvtc$(S7BxO`_Omn_v{bs zk8D?YW%+)qf6o}4uOVOHd{RqcM?2Db+E5#56K!?5rG@l$UDKr=LzblfOp>C$$hoH8 z(YyMgeuPAsU>9{Q)5MSTjiBi#j3jo^w2{G0QZcGV1NTT1nbk9L|FCNHZx$_1Z{g75 z+%DVGS^6`CH!e7H`K+wvX(i!HZh1oW<>?&364%lW3#XC%>m(LVRB-|WHN)I}m~{g4 ztzfoobAX-R7nb2fI2W#>_YT7Iun(J1!pd1yt8MkI0a}O;9RPnHql5P_U~HR6)&df> zhDjVQQ}q~`dZI5lq#)4(ixJSc1s>zzF%2R!U@{9T^P+>NoJ${wiGdP)yZ=A_oWSp2 CW_DNr literal 0 HcmV?d00001 diff --git a/ContextMenuManager/Properties/Resources/HashLnk/HashLnk_64.exe b/ContextMenuManager/Properties/Resources/HashLnk/HashLnk_64.exe new file mode 100644 index 0000000000000000000000000000000000000000..12479de85747eaaa6752f5486c0fe6c643fd8bfe GIT binary patch literal 137728 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�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

0kMmzhjtKOD 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 T+1fvbO@;5p>OYTv91r|2r~L&n literal 0 HcmV?d00001 diff --git a/ContextMenuManager/Properties/Resources/Images/CustomType.png b/ContextMenuManager/Properties/Resources/Images/Custom.png similarity index 100% rename from ContextMenuManager/Properties/Resources/Images/CustomType.png rename to ContextMenuManager/Properties/Resources/Images/Custom.png diff --git a/ContextMenuManager/Properties/Resources/Images/NewFolder.png b/ContextMenuManager/Properties/Resources/Images/NewFolder.png new file mode 100644 index 0000000000000000000000000000000000000000..7debb4468be4542a9fec256f5a1f31ce0d9dd294 GIT binary patch literal 706 zcmV;z0zLhSP)_lA+0^c!^TGbx~AOK`?@#f*_)IMR73yfx0=hf;b4i%nsT`B)Lc@ zQ3tW;pbnx-J7`NyF4f#c9Gr^C?~#ipTtZCl4T{j4LVnMEzn^^b{N4y_{E*vUF{;-A z$^zKli;Iaq19&TB>}6daS%`wY4WJmc2EfA}Mdyj*dK3^%0Hs{J1?Ed&{z0JElI5FT zAQTfLkUR>aBLL=E%CZ+=A>uZeD|KCc++9z}Qf;>&e(wyhH$mu*!;KFu(XWcdXG}Y z$}|Zv<%n5Y=CBvRWNGD~Ao&Wwcge^#^vvR*_tM5nfg$fTlLpS)TIPZCIS@dzZw%Av zlN4GiEGu_t#-`$=IP;Qvb?F}s}D^eaBwp{{3VyfkNf^NM0A_G8#x8%YyJ1`y;>8pgbNoGVoqL9`1julPQe zhc!n+0?$oHHFd9Z*%d%C4R{peR-s|q^p6*7d_=6S44=)5%7BtR!#%rcs0HT3Jdv})E zotCAQ7O4O1HO9KWk3`!5V8m@sKtFz4YrCRM-F8|8aKV2=fP*{BY>WTb?EY5(I!q)* z&_RT6aSC`w;O$9&V*!b89t&L9{NA<9kZdvmhg)Pbk$^AYS#f9WSS24K;G4cDf&T>n zH_@A6$F)|EvRJ?;7_v%eeF6gz-if~W=IR3MjR zfi&xJWxisIM+o@Ym+!ikxh20V5$N}TsNwl8W6iry%^1x`!zhA4uzJmQIEHopeW-u4U#9u>}+Oe_m8>^g=-8NR_le{lwXP(AhQj(I^6vPk14UeF0=?arYcfYU&|hu#z|I8)>>>>> 分割线 <<<<<< +SelectExtension = 请选择一个文件扩展名 +SelectPerceivedType = 请选择一个文件感知类型 +SelectDirectoryType = 请选择一个目录感知类型 +CurrentExtension = 你当前选择的文件扩展名为 %s +CurrentPerceivedType = 你当前选择的文件感知类型为 %s +CurrentDirectoryType = 你当前选择的目录感知类型为 %s +WinXSortable = 启用 WinX 菜单排序功能 +ShowFilePath = 状态栏实时显示文件路径 +OpenMoreRegedit = 允许注册表编辑器多开 [Dialog] Ok = 确认 @@ -157,9 +160,6 @@ Cancel = 取消 Browse = 浏览 Program = 程序 RegistryFile = 注册表文件 -NewShellItem = 新建Shell类型右键菜单项目 -NewSendToItem = 新建发送到子菜单项目 -NewOpenWithItem = 新建打开方式菜单项目 ItemText = 菜单文本 ItemCommand = 菜单命令 CommandArguments = 命令参数 @@ -172,9 +172,20 @@ Private = 私有 InputGuid = 输入Guid AddGuidDic = 添加Guid本地字典 DeleteGuidDic = 删除 -SelectExtension = 请选择一个文件扩展名 +TextFile = 文本文件 +DocumentFile = 文档文件 +ImageFile = 图像文件 +VideoFile = 视频文件 +AudioFile = 音频文件 +CompressedFile = 压缩文件 +SystemFile = 系统文件 +DocumentDirectory = 文档目录 +ImageDirectory = 图像目录 +VideoDirectory = 视频目录 +AudioDirectory = 音频目录 CheckReference = 请勾选你想要引用的菜单项目 CheckCopy = 请勾选你想要复制的菜单项目 +SelectGroup = 请选择保存分组 SelectSubMenuMode = 该多级菜单子项目数为0, 你有两个选择:\n①该多级菜单的所有子菜单项目私有(推荐),\n②该多级菜单可与其他多级菜单引用相同子项,\n请做出你的选择...... [MessageBox] @@ -183,10 +194,11 @@ CommandCannotBeEmpty = 菜单命令不能为空! StringParsingFailed = 本地化字符串解析失败! TextLengthCannotExceed80 = 菜单文本过长, 长度不允许超过80! ConfirmDeletePermanently = 确认是否永久删除此项?\n此操作无法还原,请谨慎操作! +DeleteButCanRestore = 确认删除此菜单的注册表项目?\n由于启用了自动备份(默认启用),\n删除后可在备份文件夹中还原。 ConfirmDeleteReference = 确认是否移除对该项目的引用? ConfirmDelete = 确认是否删除该项? -ConfirmDeleteReferenced = 确认是否永久删除此项?\n所有引用此项的项目都会失效,请谨慎操作! -CannotAddNewItem = 系统限制子菜单最大项目数为16,\n无法再进行添加! +ConfirmDeleteReferenced = 确认是否删除此项?\n所有引用此项的项目都会失效,请谨慎操作! +CannotAddNewItem = 系统限制子菜单数目最多为16,\n无法添加更多的子菜单项目! VistaUnsupportedMulti = Vista系统不支持多级菜单! CannotHideSubItem = 你的系统版本太低,不支持隐藏子级菜单! UnsupportedFilename = 不支持的文件名,\n可能已经存在相同文件名的菜单项目! @@ -203,6 +215,9 @@ FileNotExists = 文件不存在! FolderNotExists = 文件夹不存在! NoUpdateDetected = 未检测到程序更新,\n但已为你更新网络字典文件。 AuthorityProtection = 此菜单注册表项目可能受安全软件保护,\n无法对其进行禁用删除和其他个性化修改。 +WinXSorted = 为优化排序功能已对部分项目重新编号,\n需要重启文件资源管理器应用效果 +RestoreDefault = 确认还原为默认菜单项目? +DeleteGroup = 确认永久删除此组及组内所有菜单项目? [Tip] RestartExplorer = 重启Explorer会使桌面闪烁片刻, 正常现象无需担心,\n或者你也可以稍后重启或注销计算机使你的操作生效 @@ -214,14 +229,15 @@ EditSubItems = 编辑子菜单项目 AddReference = 从公共引用项目中添加引用 AddFromParentMenu = 从母菜单中复制项目 AddSeparator = 添加分隔线 -Separator = 项目分隔线 DeleteGuidDic = 删除用户自行添加的该项的本地Guid字典 LockNewMenu = 启用后可阻止第三方程序增加项目\n且可对现有项目排序(关闭后复原) CheckUpdate = 程序每月自动检测一次更新 (启动程序时)\n你可手动点击浏览Github、Gitee检查更新 LastCheckUpdateTime = 上次自动更新检查时间: OpenLanguagesDir = 打开语言文件夹 OpenDictionariesDir = 打开字典文件夹 -ShareWithSkype = 仅当用户电脑安装了 Skype 应用后有此项目 +ConfigPath = 更改配置和数据文件保存路径后,\n会导致部分已启用增强菜单失效,\n可在增强菜单中重新启用一遍 +CommandFiles = 此命令依赖配置文件,移动配置文件位置\n会导致此菜单项失效,重新启用一遍即可 +CreateGroup = 新建一个分组 [Other] RestartExplorer = 当前部分操作需要重启文件资源管理器生效 @@ -233,9 +249,9 @@ CommonItemsDictionary = 常用菜单 Translators = 翻译贡献者 OtherLanguages = 下载或上传其他语言文件 DonationList = 捐赠名单 -ConfigFile = 配置和数据文件 -SaveToAppData = 保存到 AppData 目录 -SaveToAppDir = 保存到程序所在目录 +ConfigPath = 配置和数据文件保存位置 +AppDataDir = AppData 目录 +AppDir = 程序所在目录 OpenConfigDir = 打开配置文件所在目录 AutoBackup = 删除菜单时自动备份注册表 OpenBackupDir = 打开备份文件夹 @@ -244,10 +260,10 @@ ImmediatelyCheckUpdate = 立即检查 ProtectOpenItem = 保护 "打开" 菜单项目 WebSearchEngine = 网页搜索使用的搜索引擎 CustomEngine = 自定义 -SetCustomEngine = 设定搜索引擎 (以 %s 代替搜索关键字) +SetCustomEngine = 设定搜索引擎 (以 %s 代替搜索关键词) -AboutApp = 【主要功能】\n 1.管理常见位置右键菜单\n 2.自定义添加右键菜单\n\n【兼容性能】\n 1.适用于Win7、8、8.1、10、Vista\n 2.适用于 x64、x32 CPU 操作系统\n 3.适配高分屏,最佳缩放比为150%\n\n【代码开源】\n 1.代码语言:C Sharp, Winform程序\n 2.Github仓库:https://github.com/BluePointLilac/ContextMenuManager \n 3.Gitee仓库:https://gitee.com/BluePointLilac/ContextMenuManager \n 4.使用本程序的源代码请遵守MIT开源协议\n\n【联系作者】\n 1.程序由我个人独立开发,当然也要感谢萌研社 @坑晨 平时的答疑解惑,能力有限,\n 难免出现一些Bug,欢迎大家反馈Bug和提出建议\n 2.个人B站ID:蓝点lilac https://space.bilibili.com/34492771 (欢迎大家关注我!)\n 3.个人邮箱:1617859183@qq.com\n\n【程序图标】\n 1.程序主图标来自 https://www.easyicon.net/1208132-mouse_icon.html \n 2.程序使用到的按钮图标主要来自 https://www.iconfont.cn/ \n\n【程序更新】\n 1.程序有检查更新功能,除了更新程序本身还会更新字典,下载完成后直接覆盖原文件即可\n 2.由于Github Raw被墙,Gitee Raw有每月次数访问上限,程序设置为每月检测一次更新,\n 大家也可以自己浏览Github Releases栏或Gitee 发行版栏检查是否有更新 \n\n【温馨提示】\n 1.一些特殊菜单项可能会受到其他因素影响导致不会显示在右键菜单中,\n 但是按照程序使用的通用规则在此程序中仍会显示为启用,这是正常的现象。\n 2.每个右键管理程序禁用菜单方法可能不同, 建议不要同时使用多个右键菜单管理程序,\n 大部分程序使用简单暴力的备份-删除法, 此程序尽可能使用了系统提供的键值进行隐藏操作,\n 若之前使用过其他程序禁用菜单项,请先使用对应程序还原, 不然可能无法在此程序中看到它。\n 3.此程序不用于清理未卸载干净的程序,但可帮助你定位菜单项相关注册表和文件位置,\n 你可根据相关内容进行你的操作,如果你是一个电脑小白,建议只碰启用\禁用开关。 +AboutApp = 【主要功能】\n 1.管理常见位置右键菜单\n 2.自定义添加右键菜单\n\n【兼容性能】\n 1.适用于Win7、8、8.1、10、Vista\n 2.适用于 x64、x32 CPU 操作系统\n 3.适配高分屏,最佳缩放比为150%\n\n【代码开源】\n 1.代码语言:C Sharp, Winform 程序\n 2.Github 仓库: https://github.com/BluePointLilac/ContextMenuManager \n 3.Gitee 仓库: https://gitee.com/BluePointLilac/ContextMenuManager \n\n【联系作者】\n 1.程序由我个人独立开发,当然也要感谢萌研社 @坑晨 平时的答疑解惑,能力有限,\n 难免出现一些Bug,欢迎大家反馈Bug和提出建议\n 2.个人B站: https://space.bilibili.com/34492771 (欢迎大家关注我!)\n 3.QQ邮箱: 1617859183@qq.com\n\n【资源引用】\n 1.程序主图标来自: https://www.easyicon.net/1208132-mouse_icon.html \n 2.程序按钮图标主要来自阿里巴巴矢量图标库: https://www.iconfont.cn/ \n 3.WinX HashLnk (Rafael Rivera): https://github.com/riverar/hashlnk \n\n【程序更新】\n 1.程序有检查更新功能,除了更新程序本身还会更新字典,下载完成后直接覆盖原文件即可\n 2.由于Github Raw被墙,Gitee Raw有每月次数访问上限,程序设置为每月检测一次更新,\n 大家也可以自己浏览Github Releases栏或Gitee 发行版栏检查是否有更新 \n\n【温馨提示】\n 1.一些特殊菜单项可能会受到其他因素影响导致不会显示在右键菜单中,\n 但是按照程序使用的通用规则在此程序中仍会显示为启用,这是正常的现象。\n 2.每个右键管理程序禁用菜单方法可能不同, 建议不要同时使用多个右键菜单管理程序,\n 大部分程序使用简单暴力的备份-删除法, 此程序尽可能使用了系统提供的键值进行隐藏操作,\n 若之前使用过其他程序禁用菜单项,请先使用对应程序还原, 不然可能无法在此程序中看到它。\n 3.此程序不用于清理未卸载干净的程序,但可帮助你定位菜单项相关注册表和文件位置,\n 你可根据相关内容进行你的操作,如果你是一个电脑小白,建议只碰启用\禁用开关。 -Dictionaries = 【字典说明】\n 此程序拥有几个字典文件, 每份字典又有用户字典和网络字典\n 如果想为此程序添加字典可右键保存字典文件, 并按照文件内相关说明进行添加\n 可将你的用户字典发送到我的邮箱或提交到Github为此项目做出你的贡献\n 右侧选项卡中为原始字典内容, 你可以切换选项卡进行查看\n\n【字典内容】\n 1.程序显示文本语言字典 (languages目录)\n 2.ShellEx菜单项GUID文本图标字典 (GuidInfosDic.ini)\n 3.第三方程序菜单内部设置字典 (ThirdRulesDic.xml)\n 4.增强菜单项目字典 (EnhanceMenusDic.xml) +Dictionaries = 【字典说明】\n 此程序拥有几个字典文件, 每份字典又有用户字典和网络字典\n 如果想为此程序添加字典可右键保存文件, 并按照文件内说明进行添加\n 可将你的字典发送到我的邮箱或提交到Github为此项目做出你的贡献\n 右侧选项卡中为原始字典内容, 你可以切换选项卡进行查看\n\n【字典内容】\n 1.程序显示文本语言字典 (Languages目录)\n 2.ShellEx菜单项GUID文本图标字典 (GuidInfosDic.ini)\n 3.第三方程序菜单内部设置字典 (ThirdRulesDic.xml)\n 4.增强菜单项目字典 (EnhanceMenusDic.xml) Donate = 此程序完全免费,如果你觉得这个软件对你有所帮助, 你可以通过扫描\n下方二维码(微信、支付宝、腾讯QQ)进行捐赠, 金额请随意, 谢谢支持!\n也期待你在Github或者Gitee上为此程序项目点亮Star (这对我很重要!) \ No newline at end of file diff --git a/ContextMenuManager/Properties/Resources/Texts/EnhanceMenusDic.xml b/ContextMenuManager/Properties/Resources/Texts/EnhanceMenusDic.xml index 70a785a..e47c4a8 100644 --- a/ContextMenuManager/Properties/Resources/Texts/EnhanceMenusDic.xml +++ b/ContextMenuManager/Properties/Resources/Texts/EnhanceMenusDic.xml @@ -1,18 +1,11 @@  +子元素SubKey的所有子元素是该项的子项,项名即为元素名; 每一Item项和SubKey的所有子元素的属性Default为该注册表项默认值,不放在Value\REG_SZ元素里面是为了防止与可能存在的键名为Default的键产生冲突 +由于Shell项太过复杂,程序只根据注册表项名判断存在即启用,故同一场景下不允许有相同KeyName属性的Shell项目,ShellEx项只要Guid符合则为启用--> - - - - - - - - @@ -22,21 +15,21 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - 6.2 - + - - - - + + + takeown.exe + /f "%1" /a + 6.3 - + @@ -45,31 +38,17 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - - - - - - - - - - - - - - - 6.2 + - + - - - - + + + takeown.exe + /f "%v" /a /r /d y @@ -81,7 +60,8 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 6.1 - + @@ -102,21 +82,101 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 6.2 - + 6.2 - + + + 6.2 + + + + + + wscript.exe + + + + + + + + 6.2 + + + + + + wscript.exe + + + + + + + + + + + + + wscript.exe + + + + + + - + + + cmd.exe + /s /k pushd "%v" + @@ -130,7 +190,8 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + + @@ -138,22 +199,23 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + - + - + - + 6.1 + %SystemRoot%\System32\tskill.exe @@ -161,12 +223,33 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 + + + + + + + wscript.exe + + + + + + - + @@ -175,7 +258,7 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + @@ -187,6 +270,81 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6.3 @@ -196,13 +354,182 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + - 10.0 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10.0.17763 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10.0 + + + + + + + + + + + + + wscript.exe + + + + + + + + + + + + + wscript.exe + + + + + + + + + + + + + + + + + wscript.exe + + + + + + + + + - + + + + + + + + + + + + + + + + + @@ -222,10 +549,62 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + + + + + + + + Wscript.exe + + + + + + + + + + + + + + Wscript.exe + + + + + + @@ -235,17 +614,78 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 6.2 - + + + + + + + + + - + 6.2 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -255,6 +695,151 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 + + + + + + + + + + + + + + + + + + + + %SystemRoot%\System32\gpedit.msc + + + + + + + + + + + + + + + + + + + + + + + notepad.exe + "%systemroot%\system32\drivers\etc\hosts" + + + + + + + + + + + + + + 10.0 + + + + + wscript.exe + + + + + + + + + + + + + + wscript.exe + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -262,7 +847,7 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + @@ -275,7 +860,7 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + @@ -288,7 +873,11 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + + + regsvr32.exe + "%1" + @@ -296,7 +885,11 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + + + regsvr32.exe + /u "%1" + @@ -309,7 +902,11 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + + + regsvr32.exe + "%1" + @@ -317,7 +914,11 @@ Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开 - + + + regsvr32.exe + /u "%1" + diff --git a/ContextMenuManager/Properties/Resources/Texts/GuidInfosDic.ini b/ContextMenuManager/Properties/Resources/Texts/GuidInfosDic.ini index 6cc27e0..7c51859 100644 --- a/ContextMenuManager/Properties/Resources/Texts/GuidInfosDic.ini +++ b/ContextMenuManager/Properties/Resources/Texts/GuidInfosDic.ini @@ -132,6 +132,8 @@ Icon=shell32.dll,-254 ;全部解压缩(&T)... [b8cdcb65-b1bf-4b42-9428-1dfdb7ee92af] Text=@shell32.dll,-37514 +[bd472f60-27fa-11cf-b8b4-444553540000] +Text=提取(&E)... ;复制到文件夹(&F)... [c2fbb630-2971-11d1-a18c-00c04fd75d13] Text=@*,-30304 @@ -140,8 +142,9 @@ Icon=imageres.dll,-5304 [c2fbb631-2971-11d1-a18c-00c04fd75d13] Text=@*,-30305 Icon=imageres.dll,-5303 +;窗口转换程序 [3080f90e-d7ad-11d9-bd98-0000947b0257] -Text=窗口转换程序 +Text=@shell32.dll,-12715 Icon=imageres.dll ;----------------显卡------------------ @@ -157,8 +160,11 @@ Icon=nvcpl.dll [9B5F5829-A529-4B12-814A-E81BCB8D93FC] Text=英特尔® 显卡设置 Icon=.\igfxEM.exe +[3ab1675a-ccff-11d2-8b20-00a0c93cb1f4] +Text=Intel 显卡 [5E2121EE-0300-11D4-8D3B-444553540000] Text=AMD 显卡 +Icon=.\RadeonSettings.exe ;----------------压缩------------------ [5B69A6B4-393B-459C-8EBB-214237A9E7AC] @@ -259,8 +265,8 @@ Text=清理垃圾(电脑管家) Text=使用 360杀毒 扫描 Icon=.\msdev.exe [7C0F6D57-E799-4C8A-A319-8E2B4D724CF0] -Text=360解除占用 && 强力删除 -Icon=.\360FileUnlock.exe +Text=360安全卫士 +Icon=..\360Safe.exe [5E19C0CE-C02C-46c2-98C3-A2E12EDE0E17] Text=360强力卸载 && 桌面助手 Icon=.\SoftMgr.exe @@ -271,10 +277,13 @@ Icon=.\2345MPCSafe.exe Text=使用2345软件管家卸载软件 Icon=..\..\2345SoftMgr.exe [DDEA5705-1BB0-4C03-AC1E-8FF9716A0D51] -Text=金山毒霸(64位)扫描 && 文件粉碎 +Text=金山毒霸(64位) Icon=.\kismain.exe [D21D88E8-4123-48BA-B0B1-3FDBE4AE5FA4] -Text=金山毒霸(32位)扫描 && 文件粉碎 +Text=金山毒霸(32位) +Icon=.\kismain.exe +[367f6ae2-6809-4bed-b09b-228893fb33dd] +Text=金山毒霸 Icon=.\kismain.exe [758c684b-4d10-4bc1-90da-6bebf0b4e0b4] Text=使用联想电脑管家进行扫描 @@ -304,7 +313,9 @@ Icon=.\uiStub.exe Text=Norton File Shredder [1c7593cb-c1cc-4ba7-be52-8eea47f9cb1d] Text=使用瑞星杀毒 - +[0bb81440-5f42-4480-a5f7-770a6f439fc8] +Text=IObit Malware Fighter +Icon=*,3 ;----------------传输------------------ [53D2405C-48AB-4C8A-8F59-CE0610F13BBC] Text=通过QQ发送到 @@ -370,6 +381,9 @@ Text=Huawei Share [9c5397bb-07be-4e38-98ba-395f94045091] Text=福昕PDF编辑器 Icon=..\FoxitPhantom.exe +[27be7b9d-935f-325c-9a05-63557d69f4f9] +Text=万兴PDF专家 +Icon=..\PDFExpert.exe [a6595cd1-bf77-430a-a452-18696685f7c7] Text=Adobe Acrobat Icon=..\Acrobat\Acrobat.exe @@ -439,6 +453,13 @@ Text=图片工厂(&F) Text=调整图片大小(PowerToys) [5c6a637c-9780-4d0f-a379-4732edcce7c3] Text=网易云音乐 +[1F77B17B-F531-44DB-ACA4-76ABB5010A28] +Text=AIMP +Icon=..\AIMP.exe +[8e7861bb-3a13-40a1-af25-633458757201] +Text=QQ影音 +[7a1884a3-f647-49be-b93c-8ffaf4a1f1bf] +Text=使用PP视频播放 ;----------------美化------------------ [CF444751-60FC-48B8-AC0F-363063EB2A9E] @@ -457,6 +478,12 @@ Icon=.\StartIsBackCfg.exe [6a451c0a-9597-4915-bcce-6e859bc996b2] Text=Pin to Start (Start10) Icon=.\Start10.exe +[af8fa9c9-9907-463e-bdc3-4cc1200d6310] +Text=Start Menu 8 +Icon=*,2 +[07451604-fbe4-4475-9dd6-261b7b619417] +Text=电脑管家经典开始菜单 +Icon=.\QMStart.exe [2d5ad9eb-31bc-48f7-a438-28f363632c73] Text=开启布丁桌面 Icon=.\PDLanuncher.exe @@ -481,6 +508,9 @@ Icon=.\uedit64.exe [51eee242-ad87-11d3-9c1e-0090278bbd99] Text=Vim Icon=..\vim.exe +[ed90173a-3b4c-4e7e-b9cf-79714425d4b5] +Text=PSPad Editor +Icon=.\PSPad.exe [A3777921-CFD3-4A6B-89BF-08E6B95716E8] Text=格式工厂(&F) Icon=.\FormatFactory.exe @@ -531,6 +561,9 @@ Text=FileLocator Pro Text=显示隐藏文件+扩展名 [c1b2c38f-3dca-4e3d-bc34-d5b87b636543] Text=FileMenu Tools +[189f1e63-33a7-404b-b2f6-8c76a452cc54] +Text=Smart Defrag +Icon=*,2 [4380c993-0c43-4e02-9a7a-0d40b6ea7590] Text=Defraggler Icon=.\Defraggler.exe @@ -545,4 +578,17 @@ Text=Locale Emulator Icon=.\LEGUI.exe [0a479751-02bc-11d3-a855-0004ac2568aa] Text=NTFS文件连接扩展配置工具 -Icon=.\LSEConfig.exe \ No newline at end of file +Icon=.\LSEConfig.exe +[fdf253ac-1724-4853-be34-c2dbc18fb5ca] +Text=Copywhiz +Icon=.\Copywhiz.exe +[6c467336-8281-4e60-8204-430ced96822d] +Text=共享文件夹同步 +[6fa85dad-ac32-4d74-9cba-6a1c038f9a56] +Text=文档加密/解密 +[ef479680-ea35-4ea9-b093-7114f3e3e0da] +Text=Directory Lister +Icon=.\DirListerPro.exe +[8e57c449-7891-49bb-80e5-ddac375f8ac8] +Text=使用微表格打开 +Icon=.\microexcel.exe \ No newline at end of file diff --git a/ContextMenuManager/Properties/Resources/Texts/ThirdRulesDic.xml b/ContextMenuManager/Properties/Resources/Texts/ThirdRulesDic.xml index 16c1bb3..792da79 100644 --- a/ContextMenuManager/Properties/Resources/Texts/ThirdRulesDic.xml +++ b/ContextMenuManager/Properties/Resources/Texts/ThirdRulesDic.xml @@ -1,208 +1,276 @@ - + - - - - - - - 10.0.14393 - 10.0.17763 - - - - - - - - - - - - - - - - - - - - - - 10.0.16199 - - - - - 10.0 - - - - 10.0 - - - - - 10.0 - + + + + + + + 10.0.14393 + 10.0.17763 + + + + + + + + + + + + + + + + + + + + + + + + + + 10.0.16199 + + + + + 10.0 + + + + 10.0.17134 + + + + + 6.2 + + + + + 10.0 + + + + + 6.2 + + + + + 10.0 + + + + + 10.0 + + + + 10.0 + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - - + + + - - + + - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ContextMenuManager/Properties/Settings.Designer.cs b/ContextMenuManager/Properties/Settings.Designer.cs index 4ed776d..d902879 100644 --- a/ContextMenuManager/Properties/Settings.Designer.cs +++ b/ContextMenuManager/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace ContextMenuManager.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/ContextMenuManager/Updater.cs b/ContextMenuManager/Updater.cs index a55e8d8..1c9c1dc 100644 --- a/ContextMenuManager/Updater.cs +++ b/ContextMenuManager/Updater.cs @@ -1,6 +1,5 @@ -using BulePointLilac.Methods; +using BluePointLilac.Methods; using System; -using System.Diagnostics; using System.IO; using System.Net; using System.Text; @@ -44,7 +43,8 @@ private static bool UpdateApp() if(MessageBoxEx.Show($"{AppString.MessageBox.UpdateApp}{version1}\n{info}", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { - Process.Start(reader.GetValue("Update", "Url")); + string url = reader.GetValue("Update", "Url"); + ExternalProgram.OpenUrl(url); return true; } } @@ -55,7 +55,7 @@ private static void UpdateText(string filePath, string url) { string contents = GetWebString(url); if(!contents.IsNullOrWhiteSpace()) - File.WriteAllText(filePath, contents, Encoding.UTF8); + File.WriteAllText(filePath, contents.Replace("\n", "\r\n"), Encoding.Unicode); } private static string GetWebString(string url) @@ -64,8 +64,8 @@ private static string GetWebString(string url) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); - StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8); - return reader.ReadToEnd(); + using(StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) + return reader.ReadToEnd(); } catch { return null; } } diff --git a/Donate.md b/Donate.md index ab17167..1a24440 100644 --- a/Donate.md +++ b/Donate.md @@ -8,9 +8,9 @@ ## 捐赠名单 -> 此名单不定期更新(上次更新:2021-02-04) +> 此名单不定期更新(上次更新:**2021-02-22**) -> 累计金额:**210.82** 元,累计人次:**42** 人次 +> 累计金额:**227.62** 元,累计人次:**46** 人次 |日期|用户ID|平台|金额|备注 |:--:|:--:|:--:|:--:|:--: @@ -56,3 +56,8 @@ |2020-01-28|*闯|支付宝|5| |2020-02-02|*强|微信|1| |2020-02-02|i*y|微信|5|cmm太好用了 救大命 +|2020-02-06|F*t|微信|1|感谢作者,牛年牛牛牛 +|2020-02-10|L*g|微信|2| +|2020-02-11|戴*n|微信|5|太好用了,第一次捐赠 +|2020-02-12|*奕|支付宝|2.8|真不错 +|2020-02-18|**凯|支付宝|6|方便好用,支持你 \ No newline at end of file diff --git a/README.md b/README.md index f9b33f1..fbce345 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > 一个纯粹的Windows右键菜单管理程序 ## 主要功能 -* 启用或禁用文件、文件夹、新建、发送到、打开方式、自定义文件格式等右键菜单项目 +* 启用或禁用文件、文件夹、新建、发送到、打开方式、自定义文件格式、IE浏览器、WinX等右键菜单项目 * 对上述场景右键菜单项目进行修改名称、修改图标、导航注册表位置、导航文件位置、永久删除等操作 * 对上述场景右键菜单自定义添加项目,自定义菜单命令 @@ -14,16 +14,17 @@ * 程序支持国际化多语言显示,欢迎为此程序制作语言字典 ## 运行截图 -![](https://gitee.com/BluePointLilac/ContextMenuManager/raw/master/Screenshot.png) +![](https://raw.githubusercontent.com/BluePointLilac/ContextMenuManager/master/Screenshot.png) -## 程序图标 -* 程序主图标来自 [Easyicon][1]
![](https://gitee.com/BluePointLilac/ContextMenuManager/raw/master/ContextMenuManager/Properties/AppIcon.ico) +## 资源引用 +* 程序主图标来自 [Easyicon][1]
![](https://raw.githubusercontent.com/BluePointLilac/ContextMenuManager/master/ContextMenuManager/Properties/AppIcon.ico) * [程序按钮图标][2] 主要来自于 [阿里巴巴矢量图标资源库][3] +* WinX HashLnk 作者: [Rafael Rivera][4] ## 下载更新 * 程序有检查更新功能,除了更新程序本身还会更新程序字典,下载完成后直接覆盖原文件即可 -* 由于Github Raw被墙,Gitee Raw有月访问次数上限,故将程序设置为每月自动检测一次更新,
大家也可以自行浏览 [Github Releases][4] 或者 [Gitee 发行版][5] 检查程序是否有更新。 +* 由于Github Raw被墙,Gitee Raw有月访问次数上限,故将程序设置为每月自动检测一次更新,
大家也可以自行浏览 [Github Releases][5] 或者 [Gitee 发行版][6] 检查程序是否有更新。 ## 温馨提示 * 一些特殊菜单项目(ShellEx类型,比如文件的加密(&Y))可能会受到其他因素影响,导致不会显示
在右键菜单中,但是按照程序使用的通用规则在此程序中仍会显示为启用,这是正常现象。 @@ -31,17 +32,18 @@ * 此程序不用于清理未卸载干净的程序,但是可以帮助你快速定位菜单项相关注册表位置和文件位置,
你可以根据相关内容进行你的操作。如果你是一个电脑小白,建议只使用启用\禁用功能。 ## 联系作者 -* 程序由我个人独立开发,当然也要感谢 [萌研社][6] 站长 @坑晨 平时的答疑解惑。能力有限,难免出现
一些Bug,欢迎大家积极反馈Bug和提出优化建议。 -* 个人B站:[蓝点lilac][7](欢迎大家关注我!) +* 程序由我个人独立开发,当然也要感谢 [萌研社][7] 站长 @坑晨 平时的答疑解惑。能力有限,难免出现
一些Bug,欢迎大家积极反馈Bug和提出优化建议。 +* 个人B站:[蓝点lilac][8](欢迎大家关注我!) * 个人邮箱:1617859183@qq.com ## 捐赠作者 -此程序完全免费,如果你觉得这个程序对你有所帮助,可以通过扫面下方二维码(微信、支付宝、QQ)
进行捐赠,金额请随意,谢谢你的理解和支持!更加期待你为此项目点亮Star(这对我很重要!)
![](https://gitee.com/BluePointLilac/ContextMenuManager/raw/master/ContextMenuManager/Properties/Resources/Images/Donate.png) +此程序完全免费,如果你觉得这个程序对你有所帮助,可以通过扫面下方二维码(微信、支付宝、QQ)
进行捐赠,金额请随意,谢谢你的理解和支持!更加期待你为此项目点亮Star(这对我很重要!)
![](https://raw.githubusercontent.com/BluePointLilac/ContextMenuManager/master/ContextMenuManager/Properties/Resources/Images/Donate.png) [1]: https://www.easyicon.net/1208132-mouse_icon.html [2]: https://github.com/BluePointLilac/ContextMenuManager/tree/master/ContextMenuManager/Properties/Resources/Images [3]: https://www.iconfont.cn/ - [4]: https://github.com/BluePointLilac/ContextMenuManager/releases - [5]: https://gitee.com/BluePointLilac/ContextMenuManager/releases - [6]: http://www.pcmoe.net/ - [7]: https://space.bilibili.com/34492771 \ No newline at end of file + [4]: https://github.com/riverar/hashlnk + [5]: https://github.com/BluePointLilac/ContextMenuManager/releases + [6]: https://gitee.com/BluePointLilac/ContextMenuManager/releases + [7]: http://www.pcmoe.net/ + [8]: https://space.bilibili.com/34492771 \ No newline at end of file diff --git a/languages/zh-CN.ini b/languages/zh-CN.ini index e670027..818abca 100644 --- a/languages/zh-CN.ini +++ b/languages/zh-CN.ini @@ -1,7 +1,7 @@ -;此文件为ContextMenuManager程序的显示文本字典 -;如果你想要帮助作者为此程序添加其他语言字典, 可以修改此文本并保存在.\config\languages文件夹内, -;比如美国英语字典保存为en-US.ini, 并给[General]\Language赋值 en-US English -;可以在Github或Gitee上Fork该项目并提交申请给我,或者直接发送文件到邮箱1617859183@qq.com +;此文件为 ContextMenuManager Windows右键管理程序 的显示文本字典 +;翻译:可帮助作者为此程序提供翻译并提交到Github,以下内容中等号右侧内容替换为翻译文本, +;General-Translator为翻译贡献者,General-Language为语言名称,如en-US 美国英语 +;翻译文件保存在Config\languages目录中,文件名保存为en-US.ini ;翻译说明:暂时不翻译的值保留为空即可,字典内赋值换行请使用\n进行转义 [General] @@ -35,22 +35,17 @@ WinX = Win+X LnkFile = lnk文件 UwpLnk = uwp lnk ExeFile = exe文件 -TextFile = 文本文件 -DocumentFile = 文档文件 -ImageFile = 图像文件 -VideoFile = 视频文件 -AudioFile = 音频文件 -ImageDirectory = 图像目录 -VideoDirectory = 视频目录 -AudioDirectory = 音频目录 UnknownType = 未知格式 -CustomType = 自选格式 +CustomExtension = 自选格式 +PerceivedType = 感知类型 +DirectoryType = 目录类型 EnhanceMenu = 增强菜单 ThirdRules = 三方规则 +DragDrop = 右键拖拽 PublicReferences = 公共引用 GuidBlocked = GUID 锁 - +IEMenu = IE 右键 AppSetting = 程序设置 AppLanguage = 程序语言 Dictionaries = 程序字典 @@ -71,29 +66,25 @@ Library = 所有库和库目录背景的右键菜单 New = 所有目录背景和桌面背景的右键 "新建" 的菜单项目 SendTo = 所有文件系统对象的右键 "发送到" 的菜单项目 OpenWith = 所有文件右键 "打开方式" 的菜单项目 -WinX = Win8~Win10 "开始" 按钮的右键Win+X菜单项目 +WinX = Win8~Win10 "开始" 按钮的右键 Win+X 菜单项目 -LnkFile = 所有快捷方式的右键菜单 +LnkFile = 所有LNK快捷方式的右键菜单 UwpLnk = Win8~Win10 UWP应用快捷方式的右键菜单 ExeFile = 所有EXE可执行文件的右键菜单 -TextFile = 通用文本格式 (感知类型为Text) 文件的右键菜单 -DocumentFile = 通用文档格式 (感知类型为Document) 文件的右键菜单 -ImageFile = 通用图像格式 (感知类型为Image) 文件的右键菜单 -VideoFile = 通用视频格式 (感知类型为Video) 文件的右键菜单 -AudioFile = 通用音频格式 (感知类型为Audio) 文件的右键菜单 -ImageDirectory = 通用图像文件目录的右键菜单 -VideoDirectory = 通用视频文件目录的右键菜单 -AudioDirectory = 通用音频文件目录的右键菜单 UnknownType = 所有未关联打开方式的格式文件的右键菜单 -CustomType = 自定义指定格式文件的右键菜单 +CustomExtension = 自定义指定格式文件的右键菜单 +PerceivedType = 自定义指定文件感知类型的右键菜单 +DirectoryType = 自定义指定目录感知类型的右键菜单 EnhanceMenu = 添加一些有用的菜单项 ThirdRules = 程序字典内收录的部分第三方程序内部菜单设置规则 +DragDrop = 右键拖拽文件时的菜单项目 PublicReferences = 编辑用户添加的公共引用的Shell类型子菜单项目 -GuidBlocked = 适用于部分顽固的依赖GUID(部分GUID无效)的ShellEx类型项目 +GuidBlocked = 适用于隐藏部分顽固的依赖GUID的ShellEx类型项目 +IEMenu = Internet Explorer 网页的右键菜单 [Menu] -ChangeText = 更改名称 +ChangeText = 更改文本 ItemIcon = 菜单图标 ChangeIcon = 更改图标 ShieldIcon = 盾牌图标 @@ -107,10 +98,12 @@ OtherAttributes = 其他属性 OnlyWithShift = 仅在按住 Shift 键后显示 OnlyInExplorer = 仅在 Explorer 窗口显示 NoWorkingDirectory = 不使用右键所处目录信息 -NeverDefault = 从不作为左键默认执行命令 +NeverDefault = 从不用作左键默认执行命令 +ShowAsDisabledIfHidden = 被禁用时灰色显示不隐藏 Details = 详细信息 WebSearch = 网页搜索 ChangeCommand = 更改命令 +RunAsAdministrator = 提升权限 FileProperties = 文件属性 FileLocation = 文件位置 RegistryLocation = 注册表位置 @@ -121,7 +114,10 @@ HandleGuid = 处理guid CopyGuid = 复制guid BlockGuid = 锁定guid AddGuidDic = 添加字典 -InitialData = 初始数据 +InitialData = 编辑文件初始数据 +BeforeSeparator = 显示在分割线上方 +ChangeGroup = 更换分组 +RestoreDefault = 还原默认 Edit = 编辑 Save = 保存 @@ -145,11 +141,18 @@ ShareWithSkype = 使用 Skype 共享 NewItem = 新建一个菜单项目 AddGuidBlockedItem = 添加GUID锁定项目 LockNewMenu = 锁定新建菜单 -CurrentExtension = 你当前选择的文件格式为 -SetPerceivedType = 设置文件格式感知类型 EditSubItems = 编辑 "%s" 的子菜单项目 InvalidItem = 无效菜单项目: -Separator = ●●●●●●●●●●●●●●●●●● +Separator = >>>>>> 分割线 <<<<<< +SelectExtension = 请选择一个文件扩展名 +SelectPerceivedType = 请选择一个文件感知类型 +SelectDirectoryType = 请选择一个目录感知类型 +CurrentExtension = 你当前选择的文件扩展名为 %s +CurrentPerceivedType = 你当前选择的文件感知类型为 %s +CurrentDirectoryType = 你当前选择的目录感知类型为 %s +WinXSortable = 启用 WinX 菜单排序功能 +ShowFilePath = 状态栏实时显示文件路径 +OpenMoreRegedit = 允许注册表编辑器多开 [Dialog] Ok = 确认 @@ -157,9 +160,6 @@ Cancel = 取消 Browse = 浏览 Program = 程序 RegistryFile = 注册表文件 -NewShellItem = 新建Shell类型右键菜单项目 -NewSendToItem = 新建发送到子菜单项目 -NewOpenWithItem = 新建打开方式菜单项目 ItemText = 菜单文本 ItemCommand = 菜单命令 CommandArguments = 命令参数 @@ -172,9 +172,20 @@ Private = 私有 InputGuid = 输入Guid AddGuidDic = 添加Guid本地字典 DeleteGuidDic = 删除 -SelectExtension = 请选择一个文件扩展名 +TextFile = 文本文件 +DocumentFile = 文档文件 +ImageFile = 图像文件 +VideoFile = 视频文件 +AudioFile = 音频文件 +CompressedFile = 压缩文件 +SystemFile = 系统文件 +DocumentDirectory = 文档目录 +ImageDirectory = 图像目录 +VideoDirectory = 视频目录 +AudioDirectory = 音频目录 CheckReference = 请勾选你想要引用的菜单项目 CheckCopy = 请勾选你想要复制的菜单项目 +SelectGroup = 请选择保存分组 SelectSubMenuMode = 该多级菜单子项目数为0, 你有两个选择:\n①该多级菜单的所有子菜单项目私有(推荐),\n②该多级菜单可与其他多级菜单引用相同子项,\n请做出你的选择...... [MessageBox] @@ -183,10 +194,11 @@ CommandCannotBeEmpty = 菜单命令不能为空! StringParsingFailed = 本地化字符串解析失败! TextLengthCannotExceed80 = 菜单文本过长, 长度不允许超过80! ConfirmDeletePermanently = 确认是否永久删除此项?\n此操作无法还原,请谨慎操作! +DeleteButCanRestore = 确认删除此菜单的注册表项目?\n由于启用了自动备份(默认启用),\n删除后可在备份文件夹中还原。 ConfirmDeleteReference = 确认是否移除对该项目的引用? ConfirmDelete = 确认是否删除该项? -ConfirmDeleteReferenced = 确认是否永久删除此项?\n所有引用此项的项目都会失效,请谨慎操作! -CannotAddNewItem = 系统限制子菜单最大项目数为16,\n无法再进行添加! +ConfirmDeleteReferenced = 确认是否删除此项?\n所有引用此项的项目都会失效,请谨慎操作! +CannotAddNewItem = 系统限制子菜单数目最多为16,\n无法添加更多的子菜单项目! VistaUnsupportedMulti = Vista系统不支持多级菜单! CannotHideSubItem = 你的系统版本太低,不支持隐藏子级菜单! UnsupportedFilename = 不支持的文件名,\n可能已经存在相同文件名的菜单项目! @@ -203,6 +215,9 @@ FileNotExists = 文件不存在! FolderNotExists = 文件夹不存在! NoUpdateDetected = 未检测到程序更新,\n但已为你更新网络字典文件。 AuthorityProtection = 此菜单注册表项目可能受安全软件保护,\n无法对其进行禁用删除和其他个性化修改。 +WinXSorted = 为优化排序功能已对部分项目重新编号,\n需要重启文件资源管理器应用效果 +RestoreDefault = 确认还原为默认菜单项目? +DeleteGroup = 确认永久删除此组及组内所有菜单项目? [Tip] RestartExplorer = 重启Explorer会使桌面闪烁片刻, 正常现象无需担心,\n或者你也可以稍后重启或注销计算机使你的操作生效 @@ -214,14 +229,15 @@ EditSubItems = 编辑子菜单项目 AddReference = 从公共引用项目中添加引用 AddFromParentMenu = 从母菜单中复制项目 AddSeparator = 添加分隔线 -Separator = 项目分隔线 DeleteGuidDic = 删除用户自行添加的该项的本地Guid字典 LockNewMenu = 启用后可阻止第三方程序增加项目\n且可对现有项目排序(关闭后复原) CheckUpdate = 程序每月自动检测一次更新 (启动程序时)\n你可手动点击浏览Github、Gitee检查更新 LastCheckUpdateTime = 上次自动更新检查时间: OpenLanguagesDir = 打开语言文件夹 OpenDictionariesDir = 打开字典文件夹 -ShareWithSkype = 仅当用户电脑安装了 Skype 应用后有此项目 +ConfigPath = 更改配置和数据文件保存路径后,\n会导致部分已启用增强菜单失效,\n可在增强菜单中重新启用一遍 +CommandFiles = 此命令依赖配置文件,移动配置文件位置\n会导致此菜单项失效,重新启用一遍即可 +CreateGroup = 新建一个分组 [Other] RestartExplorer = 当前部分操作需要重启文件资源管理器生效 @@ -233,9 +249,9 @@ CommonItemsDictionary = 常用菜单 Translators = 翻译贡献者 OtherLanguages = 下载或上传其他语言文件 DonationList = 捐赠名单 -ConfigFile = 配置和数据文件 -SaveToAppData = 保存到 AppData 目录 -SaveToAppDir = 保存到程序所在目录 +ConfigPath = 配置和数据文件保存位置 +AppDataDir = AppData 目录 +AppDir = 程序所在目录 OpenConfigDir = 打开配置文件所在目录 AutoBackup = 删除菜单时自动备份注册表 OpenBackupDir = 打开备份文件夹 @@ -244,10 +260,10 @@ ImmediatelyCheckUpdate = 立即检查 ProtectOpenItem = 保护 "打开" 菜单项目 WebSearchEngine = 网页搜索使用的搜索引擎 CustomEngine = 自定义 -SetCustomEngine = 设定搜索引擎 (以 %s 代替搜索关键字) +SetCustomEngine = 设定搜索引擎 (以 %s 代替搜索关键词) -AboutApp = 【主要功能】\n 1.管理常见位置右键菜单\n 2.自定义添加右键菜单\n\n【兼容性能】\n 1.适用于Win7、8、8.1、10、Vista\n 2.适用于 x64、x32 CPU 操作系统\n 3.适配高分屏,最佳缩放比为150%\n\n【代码开源】\n 1.代码语言:C Sharp, Winform程序\n 2.Github仓库:https://github.com/BluePointLilac/ContextMenuManager \n 3.Gitee仓库:https://gitee.com/BluePointLilac/ContextMenuManager \n 4.使用本程序的源代码请遵守MIT开源协议\n\n【联系作者】\n 1.程序由我个人独立开发,当然也要感谢萌研社 @坑晨 平时的答疑解惑,能力有限,\n 难免出现一些Bug,欢迎大家反馈Bug和提出建议\n 2.个人B站ID:蓝点lilac https://space.bilibili.com/34492771 (欢迎大家关注我!)\n 3.个人邮箱:1617859183@qq.com\n\n【程序图标】\n 1.程序主图标来自 https://www.easyicon.net/1208132-mouse_icon.html \n 2.程序使用到的按钮图标主要来自 https://www.iconfont.cn/ \n\n【程序更新】\n 1.程序有检查更新功能,除了更新程序本身还会更新字典,下载完成后直接覆盖原文件即可\n 2.由于Github Raw被墙,Gitee Raw有每月次数访问上限,程序设置为每月检测一次更新,\n 大家也可以自己浏览Github Releases栏或Gitee 发行版栏检查是否有更新 \n\n【温馨提示】\n 1.一些特殊菜单项可能会受到其他因素影响导致不会显示在右键菜单中,\n 但是按照程序使用的通用规则在此程序中仍会显示为启用,这是正常的现象。\n 2.每个右键管理程序禁用菜单方法可能不同, 建议不要同时使用多个右键菜单管理程序,\n 大部分程序使用简单暴力的备份-删除法, 此程序尽可能使用了系统提供的键值进行隐藏操作,\n 若之前使用过其他程序禁用菜单项,请先使用对应程序还原, 不然可能无法在此程序中看到它。\n 3.此程序不用于清理未卸载干净的程序,但可帮助你定位菜单项相关注册表和文件位置,\n 你可根据相关内容进行你的操作,如果你是一个电脑小白,建议只碰启用\禁用开关。 +AboutApp = 【主要功能】\n 1.管理常见位置右键菜单\n 2.自定义添加右键菜单\n\n【兼容性能】\n 1.适用于Win7、8、8.1、10、Vista\n 2.适用于 x64、x32 CPU 操作系统\n 3.适配高分屏,最佳缩放比为150%\n\n【代码开源】\n 1.代码语言:C Sharp, Winform 程序\n 2.Github 仓库: https://github.com/BluePointLilac/ContextMenuManager \n 3.Gitee 仓库: https://gitee.com/BluePointLilac/ContextMenuManager \n\n【联系作者】\n 1.程序由我个人独立开发,当然也要感谢萌研社 @坑晨 平时的答疑解惑,能力有限,\n 难免出现一些Bug,欢迎大家反馈Bug和提出建议\n 2.个人B站: https://space.bilibili.com/34492771 (欢迎大家关注我!)\n 3.QQ邮箱: 1617859183@qq.com\n\n【资源引用】\n 1.程序主图标来自: https://www.easyicon.net/1208132-mouse_icon.html \n 2.程序按钮图标主要来自阿里巴巴矢量图标库: https://www.iconfont.cn/ \n 3.WinX HashLnk (Rafael Rivera): https://github.com/riverar/hashlnk \n\n【程序更新】\n 1.程序有检查更新功能,除了更新程序本身还会更新字典,下载完成后直接覆盖原文件即可\n 2.由于Github Raw被墙,Gitee Raw有每月次数访问上限,程序设置为每月检测一次更新,\n 大家也可以自己浏览Github Releases栏或Gitee 发行版栏检查是否有更新 \n\n【温馨提示】\n 1.一些特殊菜单项可能会受到其他因素影响导致不会显示在右键菜单中,\n 但是按照程序使用的通用规则在此程序中仍会显示为启用,这是正常的现象。\n 2.每个右键管理程序禁用菜单方法可能不同, 建议不要同时使用多个右键菜单管理程序,\n 大部分程序使用简单暴力的备份-删除法, 此程序尽可能使用了系统提供的键值进行隐藏操作,\n 若之前使用过其他程序禁用菜单项,请先使用对应程序还原, 不然可能无法在此程序中看到它。\n 3.此程序不用于清理未卸载干净的程序,但可帮助你定位菜单项相关注册表和文件位置,\n 你可根据相关内容进行你的操作,如果你是一个电脑小白,建议只碰启用\禁用开关。 -Dictionaries = 【字典说明】\n 此程序拥有几个字典文件, 每份字典又有用户字典和网络字典\n 如果想为此程序添加字典可右键保存字典文件, 并按照文件内相关说明进行添加\n 可将你的用户字典发送到我的邮箱或提交到Github为此项目做出你的贡献\n 右侧选项卡中为原始字典内容, 你可以切换选项卡进行查看\n\n【字典内容】\n 1.程序显示文本语言字典 (languages目录)\n 2.ShellEx菜单项GUID文本图标字典 (GuidInfosDic.ini)\n 3.第三方程序菜单内部设置字典 (ThirdRulesDic.xml)\n 4.增强菜单项目字典 (EnhanceMenusDic.xml) +Dictionaries = 【字典说明】\n 此程序拥有几个字典文件, 每份字典又有用户字典和网络字典\n 如果想为此程序添加字典可右键保存文件, 并按照文件内说明进行添加\n 可将你的字典发送到我的邮箱或提交到Github为此项目做出你的贡献\n 右侧选项卡中为原始字典内容, 你可以切换选项卡进行查看\n\n【字典内容】\n 1.程序显示文本语言字典 (Languages目录)\n 2.ShellEx菜单项GUID文本图标字典 (GuidInfosDic.ini)\n 3.第三方程序菜单内部设置字典 (ThirdRulesDic.xml)\n 4.增强菜单项目字典 (EnhanceMenusDic.xml) Donate = 此程序完全免费,如果你觉得这个软件对你有所帮助, 你可以通过扫描\n下方二维码(微信、支付宝、腾讯QQ)进行捐赠, 金额请随意, 谢谢支持!\n也期待你在Github或者Gitee上为此程序项目点亮Star (这对我很重要!) \ No newline at end of file