ExportsOptionsEditorForm.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Windows.Forms;
  5. using FastReport.Utils;
  6. namespace FastReport.Forms
  7. {
  8. #if !COMMUNITY
  9. public partial class ExportsOptionsEditorForm : BaseDialogForm
  10. {
  11. #region Helper functions
  12. private void Init()
  13. {
  14. tvExportsMenu.BeginUpdate();
  15. tvExportsMenu.Nodes.Clear();
  16. List<ExportsOptions.ExportsTreeNode> menu = new List<ExportsOptions.ExportsTreeNode>();
  17. foreach (var node in options.ExportsMenu.Nodes)
  18. menu.Add(node);
  19. options.CloudMenu.Enabled = false;
  20. foreach (var node in options.CloudMenu.Nodes)
  21. {
  22. if (node.Enabled)
  23. {
  24. options.CloudMenu.Enabled = true;
  25. break;
  26. }
  27. }
  28. menu.Add(options.CloudMenu);
  29. options.MessengerMenu.Enabled = false;
  30. foreach (var node in options.MessengerMenu.Nodes)
  31. {
  32. if (node.Enabled)
  33. {
  34. options.MessengerMenu.Enabled = true;
  35. break;
  36. }
  37. }
  38. //menu.Add(options.MessengerMenu);
  39. fillExportsMenu(menu, tvExportsMenu.Nodes);
  40. tvExportsMenu.ExpandAll();
  41. tvExportsMenu.EndUpdate();
  42. }
  43. private TreeNode createTreeNode(ExportsOptions.ExportsTreeNode node)
  44. {
  45. TreeNode newNode = new TreeNode(node.ToString());
  46. // Using out of range index because there is no other way to draw node without image
  47. newNode.ImageIndex = node.ImageIndex == -1 ? 1000 : node.ImageIndex;
  48. newNode.SelectedImageIndex = newNode.ImageIndex;
  49. newNode.Tag = node;
  50. setChecked(newNode, node.Enabled);
  51. return newNode;
  52. }
  53. private void fillExportsMenu(IList<ExportsOptions.ExportsTreeNode> nodes, TreeNodeCollection currentTreeLayer)
  54. {
  55. for (int i = 0; i < nodes.Count; ++i)
  56. {
  57. currentTreeLayer.Add(createTreeNode(nodes[i]));
  58. if (nodes[i].Nodes.Count > 0)
  59. {
  60. fillExportsMenu(nodes[i].Nodes, currentTreeLayer[i].Nodes);
  61. }
  62. }
  63. }
  64. private bool isCategory(TreeNode node)
  65. {
  66. return (node.Tag as ExportsOptions.ExportsTreeNode).IsCategory;
  67. }
  68. private bool isDraggable(TreeNode node)
  69. {
  70. return (node.Tag as ExportsOptions.ExportsTreeNode).Root == ExportsOptions.GetInstance().ExportsMenu;
  71. }
  72. private bool containsNode(TreeNode targetNode, TreeNode draggedNode)
  73. {
  74. while (targetNode != null)
  75. {
  76. if (targetNode.Equals(draggedNode))
  77. {
  78. return true;
  79. }
  80. targetNode = targetNode.Parent;
  81. }
  82. return false;
  83. }
  84. private bool arePathsEqual(List<int> lhs, List<int> rhs)
  85. {
  86. if (lhs == null || rhs == null || lhs.Count != rhs.Count)
  87. {
  88. return false;
  89. }
  90. for (int i = 0; i < lhs.Count; ++i)
  91. {
  92. if (lhs[i] != rhs[i])
  93. {
  94. return false;
  95. }
  96. }
  97. return true;
  98. }
  99. private List<int> getNodePath(TreeNode node)
  100. {
  101. List<int> res = new List<int>();
  102. while (node != null)
  103. {
  104. res.Insert(0, node.Index);
  105. node = node.Parent;
  106. }
  107. return res;
  108. }
  109. private InsertionPosition getInsertionPosition(TreeNode targetNode, Point cursorPoint)
  110. {
  111. int offsetY = cursorPoint.Y - targetNode.Bounds.Y;
  112. if (isCategory(targetNode))
  113. {
  114. if (offsetY < targetNode.Bounds.Height / 3)
  115. {
  116. return InsertionPosition.Top;
  117. }
  118. else if (offsetY < (2 * targetNode.Bounds.Height) / 3)
  119. {
  120. return InsertionPosition.Inside;
  121. }
  122. else
  123. {
  124. return InsertionPosition.Bottom;
  125. }
  126. }
  127. else
  128. {
  129. if (offsetY < targetNode.Bounds.Height / 2)
  130. {
  131. return InsertionPosition.Top;
  132. }
  133. else
  134. {
  135. return InsertionPosition.Bottom;
  136. }
  137. }
  138. }
  139. private void drawInsertionLine(InsertionPosition insertionPosition, List<int> treeNodePath)
  140. {
  141. tvExportsMenu.Refresh();
  142. using (Graphics g = tvExportsMenu.CreateGraphics())
  143. {
  144. if (insertionPosition == InsertionPosition.Inside)
  145. {
  146. return;
  147. }
  148. TreeNode treeNode = null;
  149. TreeNodeCollection nodes = tvExportsMenu.Nodes;
  150. foreach (int index in treeNodePath)
  151. {
  152. treeNode = nodes[index];
  153. nodes = treeNode.Nodes;
  154. }
  155. if (treeNode != null)
  156. {
  157. int lineStartX = treeNode.Bounds.X - 50;
  158. int lineEndX = tvExportsMenu.Bounds.Width;
  159. int lineY = treeNode.Bounds.Y;
  160. if (insertionPosition == InsertionPosition.Bottom)
  161. {
  162. lineY += treeNode.Bounds.Height;
  163. }
  164. using (Pen pen = new Pen(Brushes.Black, 2))
  165. {
  166. g.DrawLine(pen, new Point(lineStartX, lineY), new Point(lineEndX, lineY));
  167. }
  168. }
  169. }
  170. }
  171. private void passChanges(TreeNodeCollection tvNodes, ExportsOptions.ExportsTreeNode.ExportsTreeNodeCollection menuNodes)
  172. {
  173. foreach (TreeNode node in tvNodes)
  174. {
  175. ExportsOptions.ExportsTreeNode menuNode = node.Tag as ExportsOptions.ExportsTreeNode;
  176. menuNode.Enabled = node.Checked;
  177. menuNode.Nodes.Clear();
  178. menuNodes.Add(menuNode);
  179. if (node.Nodes.Count > 0)
  180. {
  181. passChanges(node.Nodes, menuNode.Nodes);
  182. }
  183. }
  184. }
  185. private void passChanges(TreeNodeCollection tvNodes)
  186. {
  187. foreach (TreeNode node in tvNodes)
  188. {
  189. ExportsOptions.ExportsTreeNode menuNode = node.Tag as ExportsOptions.ExportsTreeNode;
  190. if (menuNode.Name == "Cloud")
  191. {
  192. options.CloudMenu.Enabled = node.Checked;
  193. options.CloudMenu.Nodes.Clear();
  194. passChanges(node.Nodes, menuNode.Nodes);
  195. }
  196. else if (menuNode.Name == "Messengers")
  197. {
  198. options.MessengerMenu.Enabled = node.Checked;
  199. options.MessengerMenu.Nodes.Clear();
  200. passChanges(node.Nodes, menuNode.Nodes);
  201. }
  202. else
  203. {
  204. menuNode.Enabled = node.Checked;
  205. menuNode.Nodes.Clear();
  206. options.ExportsMenu.Nodes.Add(menuNode);
  207. if (node.Nodes.Count > 0)
  208. {
  209. passChanges(node.Nodes, menuNode.Nodes);
  210. }
  211. }
  212. }
  213. }
  214. private bool isSingleChecked(TreeNode testedNode)
  215. {
  216. if (testedNode.Parent != null)
  217. {
  218. foreach (TreeNode node in testedNode.Parent.Nodes)
  219. {
  220. if (node != testedNode && node.Checked)
  221. {
  222. return false;
  223. }
  224. }
  225. return true;
  226. }
  227. return false;
  228. }
  229. // Because seting Checked property raises AfterCheck event
  230. private void setChecked(TreeNode node, bool isChecked)
  231. {
  232. tvExportsMenu.AfterCheck -= tvExportsMenu_AfterCheck;
  233. node.Checked = isChecked;
  234. tvExportsMenu.AfterCheck += tvExportsMenu_AfterCheck;
  235. }
  236. private void checkAllChilds(TreeNode node, bool isChecked)
  237. {
  238. Queue<TreeNode> queue = new Queue<TreeNode>();
  239. queue.Enqueue(node);
  240. while (queue.Count > 0)
  241. {
  242. TreeNode currentNode = queue.Dequeue();
  243. setChecked(currentNode, isChecked);
  244. foreach (TreeNode item in currentNode.Nodes)
  245. {
  246. queue.Enqueue(item);
  247. }
  248. }
  249. }
  250. #endregion
  251. private enum InsertionPosition
  252. {
  253. Top,
  254. Inside,
  255. Bottom
  256. }
  257. private ExportsOptions options = ExportsOptions.GetInstance();
  258. private InsertionPosition currentInsertionPosition;
  259. private List<int> prevNodePath;
  260. /// <summary>
  261. /// Editor for rearrangement Exports Menu elements
  262. /// </summary>
  263. public ExportsOptionsEditorForm()
  264. {
  265. InitializeComponent();
  266. Localize();
  267. Init();
  268. UIUtils.CheckRTL(this);
  269. UpdateDpiDependencies();
  270. }
  271. /// <inheritdoc/>
  272. public override void UpdateDpiDependencies()
  273. {
  274. base.UpdateDpiDependencies();
  275. tvExportsMenu.ImageList = this.GetImages();
  276. }
  277. /// <inheritdoc/>
  278. public override void Localize()
  279. {
  280. base.Localize();
  281. MyRes res = new MyRes("Export,Editor");
  282. this.Text = res.Get("");
  283. gbExportsMenu.Text = res.Get("ExportsMenu");
  284. btnDefaultSettings.Text = res.Get("DefaultMenu");
  285. }
  286. private void tvExportsMenu_ItemDrag(object sender, ItemDragEventArgs e)
  287. {
  288. DoDragDrop(e.Item, DragDropEffects.Move);
  289. }
  290. private void tvExportsMenu_DragEnter(object sender, DragEventArgs e)
  291. {
  292. if (e.Data.GetDataPresent(typeof(TreeNode)) && isDraggable(e.Data.GetData(typeof(TreeNode)) as TreeNode))
  293. {
  294. e.Effect = e.AllowedEffect;
  295. }
  296. else
  297. {
  298. e.Effect = DragDropEffects.None;
  299. }
  300. }
  301. private void tvExportsMenu_DragOver(object sender, DragEventArgs e)
  302. {
  303. Point point = tvExportsMenu.PointToClient(new Point(e.X, e.Y));
  304. TreeNode targetNode = tvExportsMenu.GetNodeAt(point) as TreeNode;
  305. TreeNode draggedNode = e.Data.GetData(typeof(TreeNode)) as TreeNode;
  306. if (targetNode != null && !containsNode(targetNode, draggedNode) && isDraggable(draggedNode) && isDraggable(targetNode))
  307. {
  308. e.Effect = DragDropEffects.Move;
  309. List<int> nodePath = getNodePath(targetNode);
  310. InsertionPosition insertionPosition = getInsertionPosition(targetNode, point);
  311. if (arePathsEqual(nodePath, prevNodePath) && insertionPosition == currentInsertionPosition)
  312. {
  313. return;
  314. }
  315. currentInsertionPosition = insertionPosition;
  316. prevNodePath = nodePath;
  317. drawInsertionLine(insertionPosition, nodePath);
  318. }
  319. else
  320. {
  321. e.Effect = DragDropEffects.None;
  322. tvExportsMenu.Refresh();
  323. }
  324. }
  325. private void tvExportsMenu_DragDrop(object sender, DragEventArgs e)
  326. {
  327. Point targetPoint = tvExportsMenu.PointToClient(new Point(e.X, e.Y));
  328. TreeNode targetNode = tvExportsMenu.GetNodeAt(targetPoint);
  329. TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
  330. if (targetNode == null)
  331. {
  332. draggedNode.Remove();
  333. tvExportsMenu.Nodes.Add(draggedNode);
  334. }
  335. else if (!containsNode(targetNode, draggedNode))
  336. {
  337. if (e.Effect == DragDropEffects.Move)
  338. {
  339. if (draggedNode.Checked == true && isSingleChecked(draggedNode))
  340. {
  341. setChecked(draggedNode.Parent, false);
  342. }
  343. draggedNode.Remove();
  344. TreeNodeCollection nodes = targetNode.Parent != null ? targetNode.Parent.Nodes : tvExportsMenu.Nodes;
  345. switch (currentInsertionPosition)
  346. {
  347. case InsertionPosition.Top:
  348. nodes.Insert(targetNode.Index, draggedNode);
  349. break;
  350. case InsertionPosition.Inside:
  351. targetNode.Nodes.Add(draggedNode);
  352. break;
  353. case InsertionPosition.Bottom:
  354. nodes.Insert(targetNode.Index + 1, draggedNode);
  355. break;
  356. }
  357. if (draggedNode.Checked == true && isSingleChecked(draggedNode))
  358. {
  359. setChecked(draggedNode.Parent, true);
  360. }
  361. }
  362. targetNode.Expand();
  363. }
  364. }
  365. private void btnOk_Click(object sender, EventArgs e)
  366. {
  367. options.ExportsMenu.Nodes.Clear();
  368. passChanges(tvExportsMenu.Nodes);
  369. options.SaveState();
  370. }
  371. private void btnDefaultSettings_Click(object sender, EventArgs e)
  372. {
  373. options.ResetToDefault();
  374. Init();
  375. }
  376. private void tvExportsMenu_AfterCheck(object sender, TreeViewEventArgs e)
  377. {
  378. if (e.Node.Checked)
  379. {
  380. TreeNode node = e.Node;
  381. while (node.Parent != null)
  382. {
  383. node = node.Parent;
  384. setChecked(node, true);
  385. }
  386. }
  387. else
  388. {
  389. if (isSingleChecked(e.Node))
  390. {
  391. setChecked(e.Node.Parent, false);
  392. }
  393. }
  394. checkAllChilds(e.Node, e.Node.Checked);
  395. }
  396. }
  397. #endif
  398. }