ShellSubMenuDialog.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. using BluePointLilac.Controls;
  2. using BluePointLilac.Methods;
  3. using ContextMenuManager.Controls.Interfaces;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Drawing;
  7. using System.IO;
  8. using System.Reflection;
  9. using System.Windows.Forms;
  10. using static Microsoft.Win32.Registry;
  11. namespace ContextMenuManager.Controls
  12. {
  13. sealed class ShellSubMenuDialog : CommonDialog
  14. {
  15. public Icon Icon { get; set; }
  16. public string Text { get; set; }
  17. public override void Reset() { }
  18. protected override bool RunDialog(IntPtr hwndOwner) { return false; }
  19. /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
  20. public void ShowDialog(string parentPath)
  21. {
  22. using(ShellSubMenuForm frm = new ShellSubMenuForm())
  23. {
  24. frm.Text = this.Text;
  25. frm.Icon = this.Icon;
  26. frm.ParentPath = parentPath;
  27. frm.ShowDialog();
  28. }
  29. }
  30. sealed class ShellSubMenuForm : Form
  31. {
  32. public ShellSubMenuForm()
  33. {
  34. this.StartPosition = FormStartPosition.CenterParent;
  35. this.ShowInTaskbar = this.MaximizeBox = this.MinimizeBox = false;
  36. this.MinimumSize = this.Size = new Size(646, 369).DpiZoom();
  37. this.Controls.AddRange(new Control[] { MlbSubItems, StatusBar });
  38. StatusBar.CanMoveForm();
  39. this.OnResize(null);
  40. }
  41. /// <summary>子菜单的父菜单的注册表路径</summary>
  42. public string ParentPath { get; set; }
  43. readonly MyListBox MlbSubItems = new MyListBox { Dock = DockStyle.Fill };
  44. readonly MyStatusBar StatusBar = new MyStatusBar();
  45. private MyList LstSubItems;
  46. protected override void OnLoad(EventArgs e)
  47. {
  48. base.OnLoad(e);
  49. bool isPublic = true;
  50. string value = GetValue(ParentPath, "SubCommands", null)?.ToString();
  51. if(value == null) isPublic = false;
  52. else if(value.IsNullOrWhiteSpace())
  53. {
  54. using(var shellKey = RegistryEx.GetRegistryKey($@"{ParentPath}\shell"))
  55. {
  56. if(shellKey != null && shellKey.GetSubKeyNames().Length > 0) isPublic = false;
  57. else
  58. {
  59. using(SubMenuModeForm frm = new SubMenuModeForm())
  60. {
  61. frm.ShowDialog();
  62. switch(frm.Mode)
  63. {
  64. case SubMenuModeForm.SubMode.Public:
  65. isPublic = true; break;
  66. case SubMenuModeForm.SubMode.Private:
  67. isPublic = false; break;
  68. case SubMenuModeForm.SubMode.None:
  69. this.Dispose(); return;
  70. }
  71. }
  72. }
  73. }
  74. }
  75. if(isPublic)
  76. {
  77. LstSubItems = new PulicMultiItemsList(MlbSubItems);
  78. ((PulicMultiItemsList)LstSubItems).LoadItems(ParentPath);
  79. this.Text += $"({AppString.Dialog.Public})";
  80. }
  81. else
  82. {
  83. LstSubItems = new PrivateMultiItemsList(MlbSubItems);
  84. ((PrivateMultiItemsList)LstSubItems).LoadItems(ParentPath);
  85. this.Text += $"({AppString.Dialog.Private})";
  86. }
  87. LstSubItems.HoveredItemChanged += (sender, a) =>
  88. {
  89. if(!AppConfig.ShowFilePath) return;
  90. MyListItem item = LstSubItems.HoveredItem;
  91. if(item is ITsiFilePathItem pathItem)
  92. {
  93. string path = pathItem.ItemFilePath;
  94. if(File.Exists(path)) { StatusBar.Text = path; return; }
  95. }
  96. StatusBar.Text = item.Text;
  97. };
  98. }
  99. sealed class SubMenuModeForm : Form
  100. {
  101. public SubMenuModeForm()
  102. {
  103. this.Text = AppString.General.AppName;
  104. this.ShowIcon = this.ShowInTaskbar = false;
  105. this.MinimizeBox = this.MaximizeBox = false;
  106. this.FormBorderStyle = FormBorderStyle.FixedSingle;
  107. this.StartPosition = FormStartPosition.CenterParent;
  108. this.Font = new Font(SystemFonts.MessageBoxFont.FontFamily, 9F);
  109. this.Controls.AddRange(new Control[] { pnlInfo, btnPrivate, btnPublic });
  110. pnlInfo.Controls.Add(lblInfo);
  111. int a = 20.DpiZoom();
  112. this.ClientSize = new Size(lblInfo.Width + 2 * a, lblInfo.Height + btnPrivate.Height + 3 * a);
  113. lblInfo.Location = new Point(a, a);
  114. pnlInfo.Height = lblInfo.Bottom + a;
  115. btnPrivate.Top = btnPublic.Top = pnlInfo.Bottom + a / 2;
  116. btnPublic.Left = pnlInfo.Width - btnPublic.Width - a;
  117. btnPrivate.Left = btnPublic.Left - btnPrivate.Width - a;
  118. btnPrivate.Click += (sender, e) => Mode = SubMode.Private;
  119. btnPublic.Click += (sender, e) => Mode = SubMode.Public;
  120. }
  121. public enum SubMode { Public, Private, None }
  122. public SubMode Mode { get; private set; } = SubMode.None;
  123. readonly Label lblInfo = new Label
  124. {
  125. Text = AppString.Dialog.SelectSubMenuMode,
  126. AutoSize = true
  127. };
  128. readonly Panel pnlInfo = new Panel
  129. {
  130. BackColor = Color.White,
  131. Dock = DockStyle.Top
  132. };
  133. readonly Button btnPrivate = new Button
  134. {
  135. Text = AppString.Dialog.Private,
  136. DialogResult = DialogResult.OK,
  137. AutoSize = true
  138. };
  139. readonly Button btnPublic = new Button
  140. {
  141. Text = AppString.Dialog.Public,
  142. DialogResult = DialogResult.OK,
  143. AutoSize = true
  144. };
  145. }
  146. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  147. sealed class PulicMultiItemsList : MyList
  148. {
  149. readonly List<string> SubKeyNames = new List<string>();
  150. /// <summary>子菜单的父菜单的注册表路径</summary>
  151. private string ParentPath { get; set; }
  152. /// <summary>菜单所处环境注册表路径</summary>
  153. private string ScenePath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ParentPath));
  154. readonly SubNewItem subNewItem = new SubNewItem(true);
  155. public PulicMultiItemsList(MyListBox owner) : base(owner)
  156. {
  157. this.AddItem(subNewItem);
  158. subNewItem.AddNewItem += (sender, e) => AddNewItem();
  159. subNewItem.AddExisting += (sender, e) => AddReference();
  160. subNewItem.AddSeparator += (sender, e) => AddSeparator();
  161. }
  162. /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
  163. public void LoadItems(string parentPath)
  164. {
  165. this.ParentPath = parentPath;
  166. string value = GetValue(ParentPath, "SubCommands", null)?.ToString();
  167. Array.ForEach(value.Split(';'), cmd => SubKeyNames.Add(cmd.TrimStart()));
  168. SubKeyNames.RemoveAll(string.IsNullOrEmpty);
  169. using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath, false, true))
  170. {
  171. foreach(string keyName in SubKeyNames)
  172. {
  173. using(var key = shellKey.OpenSubKey(keyName))
  174. {
  175. MyListItem item;
  176. if(key != null) item = new SubShellItem(this, keyName);
  177. else if(keyName == "|") item = new SeparatorItem(this);
  178. else item = new InvalidItem(this, keyName);
  179. this.AddItem(item);
  180. }
  181. }
  182. }
  183. }
  184. private void AddNewItem()
  185. {
  186. if(!SubShellTypeItem.CanAddMore(this)) return;
  187. using(NewShellDialog dlg = new NewShellDialog())
  188. {
  189. dlg.ScenePath = this.ScenePath;
  190. dlg.ShellPath = ShellItem.CommandStorePath;
  191. if(dlg.ShowDialog() != DialogResult.OK) return;
  192. SubKeyNames.Add(dlg.NewItemKeyName);
  193. SaveSorting();
  194. this.AddItem(new SubShellItem(this, dlg.NewItemKeyName));
  195. }
  196. }
  197. private void AddReference()
  198. {
  199. if(!SubShellTypeItem.CanAddMore(this)) return;
  200. using(ShellStoreDialog dlg = new ShellStoreDialog())
  201. {
  202. dlg.IsReference = true;
  203. dlg.ShellPath = ShellItem.CommandStorePath;
  204. dlg.Filter = new Func<string, bool>(itemName => !(AppConfig.HideSysStoreItems
  205. && itemName.StartsWith("Windows.", StringComparison.OrdinalIgnoreCase)));
  206. if(dlg.ShowDialog() != DialogResult.OK) return;
  207. foreach(string keyName in dlg.SelectedKeyNames)
  208. {
  209. if(!SubShellTypeItem.CanAddMore(this)) return;
  210. this.AddItem(new SubShellItem(this, keyName));
  211. this.SubKeyNames.Add(keyName);
  212. SaveSorting();
  213. }
  214. }
  215. }
  216. private void AddSeparator()
  217. {
  218. this.SubKeyNames.Add("|");
  219. SaveSorting();
  220. this.AddItem(new SeparatorItem(this));
  221. }
  222. private void SaveSorting()
  223. {
  224. SetValue(ParentPath, "SubCommands", string.Join(";", SubKeyNames.ToArray()));
  225. }
  226. private void MoveItem(MyListItem item, bool isUp)
  227. {
  228. int index = this.GetItemIndex(item);
  229. if(isUp)
  230. {
  231. if(index > 1)
  232. {
  233. this.SetItemIndex(item, index - 1);
  234. this.SubKeyNames.Reverse(index - 2, 2);
  235. }
  236. }
  237. else
  238. {
  239. if(index < this.Controls.Count - 1)
  240. {
  241. this.SetItemIndex(item, index + 1);
  242. this.SubKeyNames.Reverse(index - 1, 2);
  243. }
  244. }
  245. this.SaveSorting();
  246. }
  247. private void DeleteItem(MyListItem item)
  248. {
  249. int index = this.GetItemIndex(item);
  250. this.Controls.Remove(item);
  251. this.Controls[index - 1].Focus();
  252. this.SubKeyNames.RemoveAt(index - 1);
  253. this.SaveSorting();
  254. item.Dispose();
  255. }
  256. sealed class SubShellItem : SubShellTypeItem
  257. {
  258. public SubShellItem(PulicMultiItemsList list, string keyName) : base($@"{CommandStorePath}\{keyName}")
  259. {
  260. this.Owner = list;
  261. BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true);
  262. BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
  263. ContextMenuStrip.Items.Remove(TsiDeleteMe);
  264. ContextMenuStrip.Items.Add(TsiDeleteRef);
  265. TsiDeleteRef.Click += (sender, e) => DeleteReference();
  266. }
  267. readonly ToolStripMenuItem TsiDeleteRef = new ToolStripMenuItem(AppString.Menu.DeleteReference);
  268. public PulicMultiItemsList Owner { get; private set; }
  269. private void DeleteReference()
  270. {
  271. if(MessageBoxEx.Show(AppString.Message.ConfirmDeleteReference,
  272. MessageBoxButtons.YesNo) == DialogResult.Yes)
  273. {
  274. Owner.DeleteItem(this);
  275. }
  276. }
  277. }
  278. sealed class SeparatorItem : SubSeparatorItem
  279. {
  280. public SeparatorItem(PulicMultiItemsList list) : base()
  281. {
  282. this.Owner = list;
  283. BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true);
  284. BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
  285. }
  286. public PulicMultiItemsList Owner { get; private set; }
  287. public override void DeleteMe()
  288. {
  289. Owner.DeleteItem(this);
  290. }
  291. }
  292. sealed class InvalidItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem
  293. {
  294. public InvalidItem(PulicMultiItemsList list, string keyName)
  295. {
  296. this.Owner = list;
  297. this.Text = $"{AppString.Other.InvalidItem} {keyName}";
  298. this.Image = AppImage.NotFound.ToTransparent();
  299. BtnDelete = new DeleteButton(this);
  300. BtnMoveDown = new MoveButton(this, false);
  301. BtnMoveUp = new MoveButton(this, true);
  302. BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true);
  303. BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
  304. MyToolTip.SetToolTip(this, AppString.Tip.InvalidItem);
  305. MyToolTip.SetToolTip(BtnDelete, AppString.Menu.Delete);
  306. }
  307. public DeleteButton BtnDelete { get; set; }
  308. public PulicMultiItemsList Owner { get; private set; }
  309. public MoveButton BtnMoveUp { get; set; }
  310. public MoveButton BtnMoveDown { get; set; }
  311. public void DeleteMe()
  312. {
  313. Owner.DeleteItem(this);
  314. }
  315. }
  316. }
  317. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  318. sealed class PrivateMultiItemsList : MyList
  319. {
  320. public PrivateMultiItemsList(MyListBox owner) : base(owner)
  321. {
  322. this.AddItem(subNewItem);
  323. subNewItem.AddNewItem += (sender, e) => AddNewItem();
  324. subNewItem.AddSeparator += (sender, e) => AddSeparator();
  325. subNewItem.AddExisting += (sender, e) => AddFromParentMenu();
  326. }
  327. readonly SubNewItem subNewItem = new SubNewItem(false);
  328. /// <summary>父菜单的注册表路径</summary>
  329. public string ParentPath { get; set; }
  330. /// <summary>子菜单的Shell项注册表路径</summary>
  331. private string ShellPath { get; set; }
  332. /// <summary>父菜单的Shell项注册表路径</summary>
  333. private string ParentShellPath => RegistryEx.GetParentPath(ParentPath);
  334. /// <summary>菜单所处环境注册表路径</summary>
  335. private string ScenePath => RegistryEx.GetParentPath(ParentShellPath);
  336. /// <summary>父菜单的项名</summary>
  337. private string ParentKeyName => RegistryEx.GetKeyName(ParentPath);
  338. public void LoadItems(string parentPath)
  339. {
  340. this.ParentPath = parentPath;
  341. string sckValue = GetValue(parentPath, "ExtendedSubCommandsKey", null)?.ToString();
  342. if(!sckValue.IsNullOrWhiteSpace())
  343. {
  344. this.ShellPath = $@"{RegistryEx.CLASSESROOT}\{sckValue}\shell";
  345. }
  346. else
  347. {
  348. this.ShellPath = $@"{parentPath}\shell";
  349. }
  350. using(var shellKey = RegistryEx.GetRegistryKey(ShellPath))
  351. {
  352. if(shellKey == null) return;
  353. RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name);
  354. foreach(string keyName in shellKey.GetSubKeyNames())
  355. {
  356. string regPath = $@"{ShellPath}\{keyName}";
  357. int value = Convert.ToInt32(GetValue(regPath, "CommandFlags", 0));
  358. if(value % 16 >= 8)
  359. {
  360. this.AddItem(new SeparatorItem(this, regPath));
  361. }
  362. else
  363. {
  364. this.AddItem(new SubShellItem(this, regPath));
  365. }
  366. }
  367. }
  368. }
  369. private void AddNewItem()
  370. {
  371. if(!SubShellTypeItem.CanAddMore(this)) return;
  372. using(NewShellDialog dlg = new NewShellDialog
  373. {
  374. ScenePath = this.ScenePath,
  375. ShellPath = this.ShellPath
  376. })
  377. {
  378. if(dlg.ShowDialog() != DialogResult.OK) return;
  379. this.AddItem(new SubShellItem(this, dlg.NewItemRegPath));
  380. }
  381. }
  382. private void AddSeparator()
  383. {
  384. string regPath;
  385. if(this.Controls.Count > 1)
  386. {
  387. regPath = GetItemRegPath((MyListItem)Controls[Controls.Count - 1]);
  388. }
  389. else
  390. {
  391. regPath = $@"{ShellPath}\Item";
  392. }
  393. regPath = ObjectPath.GetNewPathWithIndex(regPath, ObjectPath.PathType.Registry);
  394. SetValue(regPath, "CommandFlags", 0x8);
  395. this.AddItem(new SeparatorItem(this, regPath));
  396. }
  397. private void AddFromParentMenu()
  398. {
  399. if(!SubShellTypeItem.CanAddMore(this)) return;
  400. using(ShellStoreDialog dlg = new ShellStoreDialog())
  401. {
  402. dlg.IsReference = false;
  403. dlg.ShellPath = this.ParentShellPath;
  404. dlg.Filter = new Func<string, bool>(itemName => !itemName.Equals(this.ParentKeyName, StringComparison.OrdinalIgnoreCase));
  405. if(dlg.ShowDialog() != DialogResult.OK) return;
  406. foreach(string keyName in dlg.SelectedKeyNames)
  407. {
  408. if(!SubShellTypeItem.CanAddMore(this)) return;
  409. string srcPath = $@"{dlg.ShellPath}\{keyName}";
  410. string dstPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry);
  411. RegistryEx.CopyTo(srcPath, dstPath);
  412. this.AddItem(new SubShellItem(this, dstPath));
  413. }
  414. }
  415. }
  416. public void MoveItem(MyListItem item, bool isUp)
  417. {
  418. int index = this.GetItemIndex(item);
  419. MyListItem otherItem = null;
  420. if(isUp)
  421. {
  422. if(index > 1)
  423. {
  424. otherItem = (MyListItem)this.Controls[index - 1];
  425. this.SetItemIndex(item, index - 1);
  426. }
  427. }
  428. else
  429. {
  430. if(index < this.Controls.Count - 1)
  431. {
  432. otherItem = (MyListItem)this.Controls[index + 1];
  433. this.SetItemIndex(item, index + 1);
  434. }
  435. }
  436. if(otherItem != null)
  437. {
  438. string path1 = GetItemRegPath(item);
  439. string path2 = GetItemRegPath(otherItem);
  440. string tempPath = ObjectPath.GetNewPathWithIndex(path1, ObjectPath.PathType.Registry);
  441. RegistryEx.MoveTo(path1, tempPath);
  442. RegistryEx.MoveTo(path2, path1);
  443. RegistryEx.MoveTo(tempPath, path2);
  444. SetItemRegPath(item, path2);
  445. SetItemRegPath(otherItem, path1);
  446. }
  447. }
  448. private string GetItemRegPath(MyListItem item)
  449. {
  450. PropertyInfo pi = item.GetType().GetProperty("RegPath");
  451. return pi.GetValue(item, null).ToString();
  452. }
  453. private void SetItemRegPath(MyListItem item, string regPath)
  454. {
  455. PropertyInfo pi = item.GetType().GetProperty("RegPath");
  456. pi.SetValue(item, regPath, null);
  457. }
  458. sealed class SubShellItem : SubShellTypeItem
  459. {
  460. public SubShellItem(PrivateMultiItemsList list, string regPath) : base(regPath)
  461. {
  462. this.Owner = list;
  463. BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true);
  464. BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
  465. SetItemTextValue();
  466. }
  467. public PrivateMultiItemsList Owner { get; private set; }
  468. private void SetItemTextValue()
  469. {
  470. using(var key = RegistryEx.GetRegistryKey(this.RegPath, true))
  471. {
  472. bool hasValue = false;
  473. foreach(string valueName in new[] { "MUIVerb", "" })
  474. {
  475. if(key.GetValue(valueName) != null)
  476. {
  477. hasValue = true; break;
  478. }
  479. }
  480. if(!hasValue) key.SetValue("MUIVerb", this.ItemText);
  481. }
  482. }
  483. }
  484. sealed class SeparatorItem : SubSeparatorItem
  485. {
  486. public SeparatorItem(PrivateMultiItemsList list, string regPath)
  487. {
  488. this.Owner = list;
  489. this.RegPath = regPath;
  490. BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true);
  491. BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
  492. }
  493. public PrivateMultiItemsList Owner { get; private set; }
  494. public string RegPath { get; private set; }
  495. public override void DeleteMe()
  496. {
  497. RegistryEx.DeleteKeyTree(this.RegPath);
  498. this.Dispose();
  499. }
  500. }
  501. }
  502. class SubSeparatorItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem
  503. {
  504. public SubSeparatorItem()
  505. {
  506. this.Text = AppString.Other.Separator;
  507. this.HasImage = false;
  508. BtnDelete = new DeleteButton(this);
  509. BtnMoveDown = new MoveButton(this, false);
  510. BtnMoveUp = new MoveButton(this, true);
  511. MyToolTip.SetToolTip(BtnDelete, AppString.Menu.Delete);
  512. }
  513. public DeleteButton BtnDelete { get; set; }
  514. public MoveButton BtnMoveUp { get; set; }
  515. public MoveButton BtnMoveDown { get; set; }
  516. public virtual void DeleteMe() { }
  517. }
  518. class SubShellTypeItem : ShellItem, IBtnMoveUpDownItem
  519. {
  520. public SubShellTypeItem(string regPath) : base(regPath)
  521. {
  522. BtnMoveDown = new MoveButton(this, false);
  523. BtnMoveUp = new MoveButton(this, true);
  524. this.SetCtrIndex(BtnMoveDown, 1);
  525. this.SetCtrIndex(BtnMoveUp, 2);
  526. }
  527. public MoveButton BtnMoveUp { get; set; }
  528. public MoveButton BtnMoveDown { get; set; }
  529. protected override bool IsSubItem => true;
  530. public static bool CanAddMore(MyList list)
  531. {
  532. int count = 0;
  533. foreach(Control item in list.Controls)
  534. {
  535. if(item.GetType().BaseType == typeof(SubShellTypeItem)) count++;
  536. }
  537. bool flag = count < 16;
  538. if(!flag) MessageBoxEx.Show(AppString.Message.CannotAddNewItem);
  539. return flag;
  540. }
  541. }
  542. sealed class SubNewItem : NewItem
  543. {
  544. public SubNewItem(bool isPublic)
  545. {
  546. this.AddCtrs(new[] { btnAddExisting, btnAddSeparator });
  547. MyToolTip.SetToolTip(btnAddExisting, isPublic ? AppString.Tip.AddReference : AppString.Tip.AddFromParentMenu);
  548. MyToolTip.SetToolTip(btnAddSeparator, AppString.Tip.AddSeparator);
  549. btnAddExisting.MouseDown += (sender, e) => AddExisting?.Invoke(null, null);
  550. btnAddSeparator.MouseDown += (sender, e) => AddSeparator?.Invoke(null, null);
  551. }
  552. readonly PictureButton btnAddExisting = new PictureButton(AppImage.AddExisting);
  553. readonly PictureButton btnAddSeparator = new PictureButton(AppImage.AddSeparator);
  554. public event EventHandler AddExisting;
  555. public event EventHandler AddSeparator;
  556. }
  557. }
  558. }
  559. }