ShellSubMenuDialog.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. using BulePointLilac.Controls;
  2. using BulePointLilac.Methods;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Windows.Forms;
  7. using static Microsoft.Win32.Registry;
  8. namespace ContextMenuManager.Controls
  9. {
  10. sealed class ShellSubMenuDialog : CommonDialog
  11. {
  12. public Icon Icon { get; set; }
  13. public string Text { get; set; }
  14. public override void Reset() { }
  15. protected override bool RunDialog(IntPtr hwndOwner) { return false; }
  16. /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
  17. public void ShowDialog(string parentPath)
  18. {
  19. using(ShellSubMenuForm frm = new ShellSubMenuForm(parentPath))
  20. {
  21. frm.Text = this.Text;
  22. frm.Icon = this.Icon;
  23. frm.ShowDialog();
  24. }
  25. }
  26. sealed class ShellSubMenuForm : Form
  27. {
  28. /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
  29. public ShellSubMenuForm(string parentPath)
  30. {
  31. this.ShowInTaskbar = false;
  32. this.StartPosition = FormStartPosition.CenterParent;
  33. this.MinimumSize = this.Size = new Size(646, 389).DpiZoom();
  34. LstSubItems = new MyListBox { Dock = DockStyle.Fill, Parent = this };
  35. string value = GetValue(parentPath, "SubCommands", null)?.ToString();
  36. if(string.IsNullOrWhiteSpace(value))
  37. {
  38. using(var shellKey = RegistryEx.GetRegistryKey($@"{parentPath}\shell"))
  39. {
  40. if(shellKey != null && shellKey.GetSubKeyNames().Length > 0)
  41. {
  42. new MultiItemsList(LstSubItems).LoadItems(parentPath);
  43. return;
  44. }
  45. else
  46. {
  47. using(SubMenuModeForm frm = new SubMenuModeForm())
  48. {
  49. frm.ShowDialog();
  50. if(frm.SubMenuMode == 1)
  51. {
  52. new MultiItemsList(LstSubItems).LoadItems(parentPath);
  53. return;
  54. }
  55. }
  56. }
  57. }
  58. }
  59. new CommonMultiItemsList(LstSubItems).LoadItems(parentPath);
  60. }
  61. readonly MyListBox LstSubItems;
  62. sealed class CommonMultiItemsList : MyList
  63. {
  64. readonly List<string> SubKeyNames = new List<string>();
  65. /// <summary>子菜单的父菜单的注册表路径</summary>
  66. private string ParentPath { get; set; }
  67. /// <summary>菜单所处环境注册表路径</summary>
  68. private string ScenePath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ParentPath));
  69. readonly NewItem newItem = new NewItem();
  70. readonly AddCommonButton btnAddCommon = new AddCommonButton();
  71. readonly PictureButton btnAddExisting = new PictureButton(AppImage.AddExisting);
  72. readonly PictureButton btnAddSeparator = new PictureButton(AppImage.AddSeparator);
  73. public CommonMultiItemsList(MyListBox owner) : base(owner)
  74. {
  75. this.AddItem(newItem);
  76. newItem.AddCtrs(new[] { btnAddCommon, btnAddExisting, btnAddSeparator });
  77. MyToolTip.SetToolTip(btnAddExisting, AppString.Tip_AddExistingItems);
  78. MyToolTip.SetToolTip(btnAddSeparator, AppString.Tip_AddSeparator);
  79. newItem.NewItemAdd += (sender, e) => AddNewItem();
  80. btnAddCommon.MouseDown += (sender, e) => AddCommonItems();
  81. btnAddExisting.MouseDown += (sender, e) => AddExistingItems();
  82. btnAddSeparator.MouseDown += (sender, e) => AddSeparator();
  83. }
  84. /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
  85. public void LoadItems(string parentPath)
  86. {
  87. this.ParentPath = parentPath;
  88. string value = GetValue(ParentPath, "SubCommands", null)?.ToString();
  89. Array.ForEach(value.Split(';'), cmd => SubKeyNames.Add(cmd.TrimStart()));
  90. SubKeyNames.RemoveAll(string.IsNullOrEmpty);
  91. using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath, false, true))
  92. {
  93. foreach(string keyName in SubKeyNames)
  94. {
  95. using(var key = shellKey.OpenSubKey(keyName))
  96. {
  97. MyListItem item;
  98. if(key != null) item = new SubShellItem(this, keyName);
  99. else if(keyName == "|") item = new SeparatorItem(this);
  100. else item = new InvalidItem(this, keyName);
  101. this.AddItem(item);
  102. }
  103. }
  104. }
  105. }
  106. private void AddNewItem()
  107. {
  108. if(!CanAddMore()) return;
  109. using(NewShellDialog dlg = new NewShellDialog
  110. {
  111. ScenePath = this.ScenePath,
  112. ShellPath = ShellItem.CommandStorePath
  113. })
  114. {
  115. if(dlg.ShowDialog() != DialogResult.OK) return;
  116. SubKeyNames.Add(dlg.NewItemKeyName);
  117. WriteRegistry();
  118. SubShellItem item = new SubShellItem(this, dlg.NewItemKeyName);
  119. this.AddItem(item);
  120. this.HoveredItem = item;
  121. }
  122. }
  123. private void AddCommonItems()
  124. {
  125. if(!CanAddMore()) return;
  126. using(ShellCommonDialog dlg = new ShellCommonDialog
  127. {
  128. ScenePath = this.ScenePath,
  129. ShellPath = ShellItem.CommandStorePath
  130. })
  131. {
  132. if(dlg.ShowDialog() == DialogResult.OK)
  133. {
  134. dlg.SelectedShellPaths.ForEach(path =>
  135. {
  136. string keyName = RegistryEx.GetKeyName(path);
  137. this.AddItem(new SubShellItem(this, keyName));
  138. SubKeyNames.Add(keyName);
  139. });
  140. WriteRegistry();
  141. }
  142. }
  143. }
  144. private void AddExistingItems()
  145. {
  146. if(!CanAddMore()) return;
  147. using(ShellStoreDialog dlg = new ShellStoreDialog())
  148. {
  149. if(dlg.ShowDialog() != DialogResult.OK) return;
  150. dlg.SelectedKeyNames.ForEach(keyName =>
  151. {
  152. SubShellItem item = new SubShellItem(this, keyName);
  153. this.AddItem(item);
  154. this.SubKeyNames.Add(keyName);
  155. WriteRegistry();
  156. });
  157. this.HoveredItem = (MyListItem)Controls[Controls.Count - 1];
  158. }
  159. }
  160. private void AddSeparator()
  161. {
  162. this.SubKeyNames.Add("|");
  163. WriteRegistry();
  164. SeparatorItem item = new SeparatorItem(this);
  165. this.AddItem(item);
  166. this.HoveredItem = item;
  167. }
  168. private bool CanAddMore()
  169. {
  170. int count = 0;
  171. foreach(Control item in Controls)
  172. {
  173. if(item.GetType() == typeof(SubShellItem)) count++;
  174. }
  175. bool flag = count < 16;
  176. if(!flag) MessageBoxEx.Show(AppString.MessageBox_CannotAddNewItem);
  177. return flag;
  178. }
  179. private void WriteRegistry()
  180. {
  181. SetValue(ParentPath, "SubCommands", string.Join(";", SubKeyNames));
  182. }
  183. private static void MoveItem(MyListItem item, CommonMultiItemsList list, bool isUp)
  184. {
  185. int index = list.GetItemIndex(item);
  186. if(isUp)
  187. {
  188. if(index > 1)
  189. {
  190. list.SetItemIndex(item, index - 1);
  191. list.SubKeyNames.Reverse(index - 2, 2);
  192. }
  193. }
  194. else
  195. {
  196. if(index < list.Controls.Count - 1)
  197. {
  198. list.SetItemIndex(item, index + 1);
  199. list.SubKeyNames.Reverse(index - 1, 2);
  200. }
  201. }
  202. list.WriteRegistry();
  203. }
  204. private static void RemoveItem(CommonMultiItemsList list, MyListItem item)
  205. {
  206. int index = list.GetItemIndex(item);
  207. list.Controls.Remove(item);
  208. list.Controls[index - 1].Focus();
  209. list.SubKeyNames.RemoveAt(index - 1);
  210. list.WriteRegistry();
  211. item.Dispose();
  212. }
  213. sealed class SubShellItem : ShellItem, IBtnMoveUpDownItem
  214. {
  215. public SubShellItem(CommonMultiItemsList list, string keyName) : base($@"{CommandStorePath}\{keyName}")
  216. {
  217. this.Owner = list;
  218. BtnMoveDown = new MoveButton(this, false);
  219. BtnMoveUp = new MoveButton(this, true);
  220. BtnMoveUp.MouseDown += (sender, e) => MoveItem(this, Owner, true);
  221. BtnMoveDown.MouseDown += (sender, e) => MoveItem(this, Owner, false);
  222. ContextMenuStrip.Items.Remove(TsiDeleteMe);
  223. ContextMenuStrip.Items.Add(TsiDeleteRef);
  224. TsiDeleteRef.Click += (sender, e) => DeleteReference();
  225. }
  226. protected override bool IsSubItem => true;
  227. readonly ToolStripMenuItem TsiDeleteRef = new ToolStripMenuItem(AppString.Menu_DeleteReference);
  228. public CommonMultiItemsList Owner { get; private set; }
  229. public MoveButton BtnMoveUp { get; set; }
  230. public MoveButton BtnMoveDown { get; set; }
  231. private void DeleteReference()
  232. {
  233. if(MessageBoxEx.Show(AppString.MessageBox_ConfirmDeleteReference,
  234. MessageBoxButtons.YesNo) == DialogResult.Yes)
  235. {
  236. RemoveItem(Owner, this);
  237. }
  238. }
  239. public override void DeleteMe()
  240. {
  241. if(MessageBoxEx.Show(AppString.MessageBox_ConfirmDeleteReferenced,
  242. MessageBoxButtons.YesNo) == DialogResult.Yes) base.DeleteMe();
  243. }
  244. }
  245. sealed class SeparatorItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem
  246. {
  247. public SeparatorItem(CommonMultiItemsList list)
  248. {
  249. this.Owner = list;
  250. this.Text = AppString.Text_Separator;
  251. this.Image = AppImage.Separator;
  252. BtnDelete = new DeleteButton(this);
  253. BtnMoveDown = new MoveButton(this, false);
  254. BtnMoveUp = new MoveButton(this, true);
  255. BtnMoveUp.MouseDown += (sender, e) => MoveItem(this, Owner, true);
  256. BtnMoveDown.MouseDown += (sender, e) => MoveItem(this, Owner, false);
  257. MyToolTip.SetToolTip(BtnDelete, AppString.Tip_Separator);
  258. }
  259. public DeleteButton BtnDelete { get; set; }
  260. public CommonMultiItemsList Owner { get; private set; }
  261. public MoveButton BtnMoveUp { get; set; }
  262. public MoveButton BtnMoveDown { get; set; }
  263. public void DeleteMe()
  264. {
  265. RemoveItem(Owner, this);
  266. this.Dispose();
  267. }
  268. }
  269. sealed class InvalidItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem
  270. {
  271. public InvalidItem(CommonMultiItemsList list, string keyName)
  272. {
  273. this.Owner = list;
  274. this.Text = $"{AppString.Text_InvalidItem} {keyName}";
  275. this.Image = AppImage.NotFound.ToTransparent();
  276. BtnDelete = new DeleteButton(this);
  277. BtnMoveDown = new MoveButton(this, false);
  278. BtnMoveUp = new MoveButton(this, true);
  279. BtnMoveUp.MouseDown += (sender, e) => MoveItem(this, Owner, true);
  280. BtnMoveDown.MouseDown += (sender, e) => MoveItem(this, Owner, false);
  281. MyToolTip.SetToolTip(BtnDelete, AppString.Tip_InvalidItem);
  282. }
  283. public DeleteButton BtnDelete { get; set; }
  284. public CommonMultiItemsList Owner { get; private set; }
  285. public MoveButton BtnMoveUp { get; set; }
  286. public MoveButton BtnMoveDown { get; set; }
  287. public void DeleteMe()
  288. {
  289. RemoveItem(Owner, this);
  290. this.Dispose();
  291. }
  292. }
  293. }
  294. sealed class MultiItemsList : MyList
  295. {
  296. public MultiItemsList(MyListBox owner) : base(owner)
  297. {
  298. this.AddItem(newItem);
  299. newItem.AddCtr(btnAddCommon);
  300. newItem.NewItemAdd += (sender, e) => AddNewItem();
  301. btnAddCommon.MouseDown += (sender, e) => AddCommonItems();
  302. }
  303. readonly NewItem newItem = new NewItem();
  304. readonly AddCommonButton btnAddCommon = new AddCommonButton();
  305. /// <summary>子菜单的父菜单的注册表路径</summary>
  306. public string ParentPath { get; set; }
  307. /// <summary>子菜单的Shell项注册表路径</summary>
  308. private string ShellPath => $@"{ParentPath}\shell";
  309. /// <summary>菜单所处环境注册表路径</summary>
  310. private string ScenePath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ParentPath));
  311. public void LoadItems(string parentPath)
  312. {
  313. this.ParentPath = parentPath;
  314. using(var shellKey = RegistryEx.GetRegistryKey(ShellPath))
  315. {
  316. if(shellKey == null) return;
  317. RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name);
  318. Array.ForEach(shellKey.GetSubKeyNames(), keyName =>
  319. {
  320. this.AddItem(new SubShellItem($@"{ShellPath}\{keyName}"));
  321. });
  322. }
  323. }
  324. private void AddNewItem()
  325. {
  326. if(!CanAddMore()) return;
  327. using(NewShellDialog dlg = new NewShellDialog
  328. {
  329. ScenePath = this.ScenePath,
  330. ShellPath = this.ShellPath
  331. })
  332. {
  333. if(dlg.ShowDialog() == DialogResult.OK)
  334. this.InsertItem(new SubShellItem(dlg.NewItemRegPath), GetItemIndex(newItem) + 1);
  335. }
  336. }
  337. private void AddCommonItems()
  338. {
  339. if(!CanAddMore()) return;
  340. using(ShellCommonDialog dlg = new ShellCommonDialog
  341. {
  342. ScenePath = this.ScenePath,
  343. ShellPath = this.ShellPath
  344. })
  345. {
  346. if(dlg.ShowDialog() == DialogResult.OK)
  347. {
  348. dlg.SelectedShellPaths.ForEach(path => this.AddItem(new SubShellItem(path)));
  349. this.SortItemByText();
  350. this.SetItemIndex(newItem, 0);
  351. }
  352. }
  353. }
  354. private bool CanAddMore()
  355. {
  356. int count = 0;
  357. foreach(Control item in Controls)
  358. {
  359. if(item.GetType() == typeof(SubShellItem)) count++;
  360. }
  361. bool flag = count < 16;
  362. if(!flag) MessageBoxEx.Show(AppString.MessageBox_CannotAddNewItem);
  363. return flag;
  364. }
  365. sealed class SubShellItem : ShellItem
  366. {
  367. public SubShellItem(string regPath) : base(regPath)
  368. {
  369. TsiOtherAttributes.DropDownItems.Add(tsiShowSeparator);
  370. tsiShowSeparator.Click += (sender, e) => ShowSeparator = !tsiShowSeparator.Checked;
  371. ContextMenuStrip.Opening += (sender, e) => tsiShowSeparator.Checked = ShowSeparator;
  372. }
  373. protected override bool IsSubItem => true;
  374. private bool ShowSeparator
  375. {
  376. get
  377. {
  378. int value = Convert.ToInt32(GetValue(RegPath, "CommandFlags", 0)) % 64;
  379. return value >= 32 && value < 56;
  380. }
  381. set
  382. {
  383. if(value) SetValue(RegPath, "CommandFlags", 32, Microsoft.Win32.RegistryValueKind.DWord);
  384. else RegistryEx.DeleteValue(RegPath, "CommandFlags");
  385. }
  386. }
  387. readonly ToolStripMenuItem tsiShowSeparator = new ToolStripMenuItem(AppString.Menu_ShowSeparator);
  388. }
  389. }
  390. }
  391. }
  392. }