TreeView.cs 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. //
  20. // Copyright (c) 2004-2005 Novell, Inc.
  21. //
  22. // Authors:
  23. // Jackson Harper ([email protected])
  24. // Kazuki Oikawa ([email protected])
  25. using System;
  26. using System.Drawing;
  27. using System.Drawing.Drawing2D;
  28. using System.Collections;
  29. namespace System.Windows.Forms {
  30. public class TreeView : Control {
  31. private string path_separator = "\\";
  32. private int item_height = -1;
  33. private bool sorted;
  34. private TreeNode top_node;
  35. internal TreeNode root_node;
  36. private TreeNodeCollection nodes;
  37. private int total_node_count;
  38. private TreeNode selected_node = null;
  39. private TreeNode focused_node = null;
  40. private bool select_mmove = false;
  41. private ImageList image_list;
  42. private int image_index = -1;
  43. private int selected_image_index = -1;
  44. private bool full_row_select;
  45. private bool hot_tracking;
  46. private int indent = 19;
  47. private bool checkboxes;
  48. private bool label_edit;
  49. private bool scrollable;
  50. private bool show_lines = true;
  51. private bool show_root_lines = true;
  52. private bool show_plus_minus = true;
  53. private bool hide_selection = true;
  54. private bool add_hscroll;
  55. private bool add_vscroll;
  56. private int max_node_width;
  57. private VScrollBar vbar;
  58. private bool vbar_added;
  59. private int skipped_nodes;
  60. private HScrollBar hbar;
  61. private bool hbar_added;
  62. private int hbar_offset;
  63. private SizeGrip grip;
  64. private bool grip_added;
  65. private int update_stack;
  66. private TreeViewEventHandler on_after_check;
  67. private TreeViewEventHandler on_after_collapse;
  68. private TreeViewEventHandler on_after_expand;
  69. private NodeLabelEditEventHandler on_after_label_edit;
  70. private TreeViewEventHandler on_after_select;
  71. private TreeViewCancelEventHandler on_before_check;
  72. private TreeViewCancelEventHandler on_before_collapse;
  73. private TreeViewCancelEventHandler on_before_expand;
  74. private NodeLabelEditEventHandler on_before_label_edit;
  75. private TreeViewCancelEventHandler on_before_select;
  76. private Pen dash;
  77. private int open_node_count = -1;
  78. public TreeView ()
  79. {
  80. base.background_color = ThemeEngine.Current.ColorWindow;
  81. base.foreground_color = ThemeEngine.Current.ColorWindowText;
  82. root_node = new TreeNode (this);
  83. root_node.Text = "ROOT NODE";
  84. nodes = new TreeNodeCollection (root_node);
  85. root_node.SetNodes (nodes);
  86. MouseDown += new MouseEventHandler (MouseDownHandler);
  87. MouseUp += new MouseEventHandler(MouseUpHandler);
  88. MouseMove += new MouseEventHandler(MouseMoveHandler);
  89. SizeChanged += new EventHandler (SizeChangedHandler);
  90. SetStyle (ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
  91. SetStyle (ControlStyles.UserPaint | ControlStyles.Selectable, true);
  92. dash = new Pen (SystemColors.ControlLight, 1);
  93. }
  94. public string PathSeparator {
  95. get { return path_separator; }
  96. set { path_separator = value; }
  97. }
  98. public bool HideSelection {
  99. get { return hide_selection; }
  100. set {
  101. if (hide_selection == value)
  102. return;
  103. hide_selection = value;
  104. this.Refresh ();
  105. }
  106. }
  107. public bool Sorted {
  108. get { return sorted; }
  109. set {
  110. if (sorted != value)
  111. sorted = value;
  112. if (sorted)
  113. Nodes.Sort ();
  114. }
  115. }
  116. public TreeNode TopNode {
  117. get { return top_node; }
  118. }
  119. public TreeNodeCollection Nodes {
  120. get { return nodes; }
  121. }
  122. public int ItemHeight {
  123. get {
  124. if (item_height == -1)
  125. return FontHeight + 3;
  126. return item_height;
  127. }
  128. set {
  129. if (value == item_height)
  130. return;
  131. item_height = value;
  132. Refresh ();
  133. }
  134. }
  135. public int VisibleCount {
  136. get {
  137. return ClientRectangle.Height / ItemHeight;
  138. }
  139. }
  140. [MonoTODO ("Anything special need to be done here?")]
  141. public ImageList ImageList {
  142. get { return image_list; }
  143. set { image_list = value; }
  144. }
  145. public int ImageIndex {
  146. get { return image_index; }
  147. set {
  148. if (value < -1) {
  149. throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
  150. "'value' must be greater than or equal to 0.");
  151. }
  152. image_index = value;
  153. }
  154. }
  155. public int SelectedImageIndex {
  156. get { return selected_image_index; }
  157. set {
  158. if (value < -1) {
  159. throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
  160. "'value' must be greater than or equal to 0.");
  161. }
  162. }
  163. }
  164. public TreeNode SelectedNode {
  165. get { return selected_node; }
  166. set {
  167. if (selected_node == value)
  168. return;
  169. TreeViewCancelEventArgs e = new TreeViewCancelEventArgs (value, false, TreeViewAction.Unknown);
  170. OnBeforeSelect (e);
  171. if (e.Cancel)
  172. return;
  173. selected_node = value;
  174. focused_node = value;
  175. Refresh ();
  176. OnAfterSelect (new TreeViewEventArgs (value, TreeViewAction.Unknown));
  177. }
  178. }
  179. public override Color BackColor {
  180. get { return base.BackColor;}
  181. set { base.BackColor = value; }
  182. }
  183. public override Image BackgroundImage {
  184. get { return base.BackgroundImage; }
  185. set { base.BackgroundImage = value; }
  186. }
  187. /*
  188. Commented out until this is implemented in Control
  189. public override BorderStyle BorderStyle {
  190. get { return base.BorderStyle; }
  191. set { base.BorderStyle = value; }
  192. }
  193. */
  194. public override Color ForeColor {
  195. get { return base.ForeColor; }
  196. set { base.ForeColor = value; }
  197. }
  198. public override string Text {
  199. get { return base.Text; }
  200. set { base.Text = value; }
  201. }
  202. public bool FullRowSelect {
  203. get { return full_row_select; }
  204. set {
  205. if (value == full_row_select)
  206. return;
  207. full_row_select = value;
  208. Refresh ();
  209. }
  210. }
  211. public bool HotTracking {
  212. get { return hot_tracking; }
  213. set { hot_tracking = value; }
  214. }
  215. public int Indent {
  216. get { return indent; }
  217. set {
  218. if (indent == value)
  219. return;
  220. if (value > 32000) {
  221. throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
  222. "'Indent' must be less than or equal to 32000");
  223. }
  224. if (value < 0) {
  225. throw new ArgumentException ("'" + value + "' is not a valid value for 'Indent'. " +
  226. "'Indent' must be greater than or equal to 0.");
  227. }
  228. indent = value;
  229. Refresh ();
  230. }
  231. }
  232. public bool LabelEdit {
  233. get { return label_edit; }
  234. set { label_edit = value; }
  235. }
  236. public bool Scrollable {
  237. get { return scrollable; }
  238. set {
  239. if (scrollable == value)
  240. return;
  241. scrollable = value;
  242. }
  243. }
  244. public bool ShowLines {
  245. get { return show_lines; }
  246. set {
  247. if (show_lines == value)
  248. return;
  249. show_lines = value;
  250. Refresh ();
  251. }
  252. }
  253. public bool ShowRootLines {
  254. get { return show_root_lines; }
  255. set {
  256. if (show_root_lines == value)
  257. return;
  258. show_root_lines = value;
  259. Refresh ();
  260. }
  261. }
  262. public bool CheckBoxes {
  263. get { return checkboxes; }
  264. set {
  265. if (value == checkboxes)
  266. return;
  267. checkboxes = value;
  268. // Match a "bug" in the MS implementation where disabling checkboxes
  269. // collapses the entire tree, but enabling them does not affect the
  270. // state of the tree.
  271. if (!checkboxes)
  272. root_node.CollapseAllUncheck ();
  273. Refresh ();
  274. }
  275. }
  276. public bool ShowPlusMinus {
  277. get { return show_plus_minus; }
  278. set {
  279. if (show_plus_minus == value)
  280. return;
  281. show_plus_minus = value;
  282. Refresh ();
  283. }
  284. }
  285. [MonoTODO ("Anything extra needed here")]
  286. protected override CreateParams CreateParams {
  287. get {
  288. CreateParams cp = base.CreateParams;
  289. return cp;
  290. }
  291. }
  292. protected override Size DefaultSize {
  293. get { return new Size (121, 97); }
  294. }
  295. internal int TotalNodeCount {
  296. get { return total_node_count; }
  297. set { total_node_count = value; }
  298. }
  299. // TODO: we shouldn't have to compute this on the fly
  300. private Rectangle ViewportRectangle {
  301. get {
  302. Rectangle res = ClientRectangle;
  303. if (vbar != null && vbar.Visible)
  304. res.Width -= vbar.Width;
  305. if (hbar != null && hbar.Visible)
  306. res.Height -= hbar.Height;
  307. return res;
  308. }
  309. }
  310. protected override void CreateHandle ()
  311. {
  312. base.CreateHandle ();
  313. }
  314. protected override void Dispose (bool disposing)
  315. {
  316. if (disposing) {
  317. if (image_list != null)
  318. image_list.Dispose ();
  319. }
  320. base.Dispose (disposing);
  321. }
  322. [MonoTODO ("What does the state effect?")]
  323. protected OwnerDrawPropertyBag GetItemRenderStyles (TreeNode node, int state)
  324. {
  325. return node.prop_bag;
  326. }
  327. [MonoTODO ("Need to know if we are editing, not if editing is enabled")]
  328. protected override bool IsInputKey (Keys key_data)
  329. {
  330. if (label_edit && (key_data & Keys.Alt) == 0)
  331. {
  332. switch (key_data & Keys.KeyCode) {
  333. case Keys.Enter:
  334. case Keys.Escape:
  335. case Keys.Prior:
  336. case Keys.Next:
  337. case Keys.End:
  338. case Keys.Home:
  339. case Keys.Left:
  340. case Keys.Up:
  341. case Keys.Right:
  342. case Keys.Down:
  343. return true;
  344. }
  345. }
  346. return base.IsInputKey (key_data);
  347. }
  348. protected virtual void OnAfterCheck (TreeViewEventArgs e)
  349. {
  350. if (on_after_check != null)
  351. on_after_check (this, e);
  352. }
  353. protected internal virtual void OnAfterCollapse (TreeViewEventArgs e)
  354. {
  355. if (on_after_collapse != null)
  356. on_after_collapse (this, e);
  357. }
  358. protected internal virtual void OnAfterExpand (TreeViewEventArgs e)
  359. {
  360. if (on_after_expand != null)
  361. on_after_expand (this, e);
  362. }
  363. protected virtual void OnAfterLabelEdit (NodeLabelEditEventArgs e)
  364. {
  365. if (on_after_label_edit != null)
  366. on_after_label_edit (this, e);
  367. }
  368. protected virtual void OnAfterSelect (TreeViewEventArgs e)
  369. {
  370. if (on_after_select != null)
  371. on_after_select (this, e);
  372. }
  373. protected virtual void OnBeforeCheck (TreeViewCancelEventArgs e)
  374. {
  375. if (on_before_check != null)
  376. on_before_check (this, e);
  377. }
  378. protected internal virtual void OnBeforeCollapse (TreeViewCancelEventArgs e)
  379. {
  380. if (on_before_collapse != null)
  381. on_before_collapse (this, e);
  382. }
  383. protected internal virtual void OnBeforeExpand (TreeViewCancelEventArgs e)
  384. {
  385. if (on_before_expand != null)
  386. on_before_expand (this, e);
  387. }
  388. protected virtual void OnBeforeLabelEdit (NodeLabelEditEventArgs e)
  389. {
  390. if (on_before_label_edit != null)
  391. on_before_label_edit (this, e);
  392. }
  393. protected virtual void OnBeforeSelect (TreeViewCancelEventArgs e)
  394. {
  395. if (on_before_select != null)
  396. on_before_select (this, e);
  397. }
  398. protected override void OnHandleCreated (EventArgs e)
  399. {
  400. base.OnHandleCreated (e);
  401. }
  402. protected override void OnHandleDestroyed (EventArgs e)
  403. {
  404. base.OnHandleDestroyed (e);
  405. }
  406. public void BeginUpdate ()
  407. {
  408. if (!IsHandleCreated)
  409. return;
  410. update_stack++;
  411. }
  412. public void EndUpdate ()
  413. {
  414. if (!IsHandleCreated)
  415. return;
  416. if (update_stack > 1) {
  417. update_stack--;
  418. } else {
  419. update_stack = 0;
  420. Refresh ();
  421. }
  422. }
  423. public void ExpandAll ()
  424. {
  425. root_node.ExpandAll ();
  426. }
  427. public void CollapseAll ()
  428. {
  429. root_node.CollapseAll ();
  430. }
  431. public TreeNode GetNodeAt (Point pt)
  432. {
  433. return GetNodeAt (pt.X, pt.Y);
  434. }
  435. public TreeNode GetNodeAt (int x, int y)
  436. {
  437. TreeNode node = GetNodeAt (y);
  438. if (node == null || !IsTextArea (node, x))
  439. return null;
  440. return node;
  441. }
  442. private TreeNode GetNodeAt (int y)
  443. {
  444. if (top_node == null)
  445. top_node = nodes [0];
  446. OpenTreeNodeEnumerator o = new OpenTreeNodeEnumerator (TopNode);
  447. int move = y / ItemHeight + skipped_nodes;
  448. for (int i = -1; i < move; i++) {
  449. if (!o.MoveNext ())
  450. return null;
  451. }
  452. return o.CurrentNode;
  453. }
  454. private bool IsTextArea (TreeNode node, int x)
  455. {
  456. return node != null && node.Bounds.Left <= x && node.Bounds.Right >= x;
  457. }
  458. public int GetNodeCount (bool include_subtrees)
  459. {
  460. return root_node.GetNodeCount (include_subtrees);
  461. }
  462. public override string ToString ()
  463. {
  464. int count = Nodes.Count;
  465. if (count < 0)
  466. return String.Concat (base.ToString (), "Node Count: 0");
  467. return String.Concat (base.ToString (), "Node Count: ", count, " Nodes[0]: ", Nodes [0]);
  468. }
  469. protected override void WndProc(ref Message m)
  470. {
  471. switch ((Msg) m.Msg) {
  472. case Msg.WM_PAINT: {
  473. PaintEventArgs paint_event;
  474. paint_event = XplatUI.PaintEventStart (Handle);
  475. DoPaint (paint_event);
  476. XplatUI.PaintEventEnd (Handle);
  477. return;
  478. }
  479. case Msg.WM_LBUTTONDBLCLK:
  480. int val = m.LParam.ToInt32();
  481. DoubleClickHandler (null, new MouseEventArgs (MouseButtons.Left, 2, val & 0xffff, (val>>16) & 0xffff, 0));
  482. break;
  483. }
  484. base.WndProc (ref m);
  485. }
  486. // TODO: Update from supplied node down
  487. internal void UpdateBelow (TreeNode node)
  488. {
  489. // We need to update the current node so the plus/minus block gets update too
  490. Rectangle invalid = new Rectangle (0, node.Bounds.Top, Width, Height - node.Bounds.Top);
  491. Invalidate (invalid);
  492. }
  493. internal void UpdateNode (TreeNode node)
  494. {
  495. Rectangle invalid = new Rectangle (0, node.Bounds.Top, Width, node.Bounds.Height);
  496. Invalidate (invalid);
  497. }
  498. int draw_count = 0;
  499. private void DoPaint (PaintEventArgs pe)
  500. {
  501. if (Width <= 0 || Height <= 0 || Visible == false)
  502. return;
  503. Draw (pe.ClipRectangle);
  504. pe.Graphics.DrawImage (ImageBuffer, pe.ClipRectangle, pe.ClipRectangle, GraphicsUnit.Pixel);
  505. }
  506. private void Draw (Rectangle clip)
  507. {
  508. DateTime start = DateTime.Now;
  509. if (top_node == null && Nodes.Count > 0)
  510. top_node = nodes [0];
  511. // Decide if we need a scrollbar
  512. int old_open_node_count = open_node_count;
  513. open_node_count = GetOpenNodeCount ();
  514. int node_count = 0;
  515. Rectangle fill = ClientRectangle;
  516. add_vscroll = false;
  517. add_hscroll = false;
  518. add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height;
  519. DeviceContext.FillRectangle (new SolidBrush (BackColor), fill);
  520. int depth = 0;
  521. int item_height = ItemHeight;
  522. Font font = Font;
  523. int height = ClientRectangle.Height;
  524. int visible_node_count = 0;
  525. foreach (TreeNode node in nodes) {
  526. DrawNode (node, clip, ref depth, ref node_count, item_height,
  527. font, ref visible_node_count, height);
  528. depth = 0;
  529. }
  530. if (max_node_width > ClientRectangle.Width)
  531. add_hscroll = true;
  532. if (add_vscroll)
  533. add_hscroll = max_node_width > ClientRectangle.Width - ThemeEngine.Current.VScrollBarDefaultSize.Width;
  534. if (add_hscroll)
  535. add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height - ThemeEngine.Current.HScrollBarDefaultSize.Width;
  536. if (add_hscroll) {
  537. AddHorizontalScrollBar ();
  538. } else if (hbar != null) {
  539. hbar_offset = 0;
  540. hbar.Visible = false;
  541. }
  542. if (add_vscroll) {
  543. AddVerticalScrollBar (open_node_count, old_open_node_count != open_node_count);
  544. } else if (vbar != null) {
  545. vbar.Visible = false;
  546. skipped_nodes = 0;
  547. }
  548. if (add_hscroll && add_vscroll)
  549. AddSizeGrip ();
  550. else if (grip != null)
  551. grip.Visible = false;
  552. /*
  553. Console.WriteLine ("treeview drawing time: " + (DateTime.Now - start));
  554. Console.WriteLine ("node count: " + node_count);
  555. Console.WriteLine ("total node count: " + total_node_count);
  556. */
  557. }
  558. private void DumpNode (TreeNode node, ref int depth)
  559. {
  560. for (int i = 0; i < depth; i++)
  561. Console.Write ("****");
  562. Console.WriteLine (node.Text);
  563. if (node.PrevNode != null)
  564. Console.WriteLine (" -- " + node.PrevNode.Text);
  565. depth++;
  566. foreach (TreeNode child in node.Nodes) {
  567. DumpNode (child, ref depth);
  568. }
  569. depth--;
  570. }
  571. private void DrawNodePlusMinus (TreeNode node, int x, int y, int middle)
  572. {
  573. node.UpdatePlusMinusBounds (x, middle - 4, 8, 8);
  574. DeviceContext.DrawRectangle (SystemPens.ControlDark, node.PlusMinusBounds);
  575. if (node.IsExpanded) {
  576. DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
  577. } else {
  578. DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
  579. DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 4, middle - 2, x + 4, middle + 2);
  580. }
  581. }
  582. private void DrawNodeCheckBox (TreeNode node, int x, int y)
  583. {
  584. int offset = (ItemHeight - 13);
  585. node.UpdateCheckBoxBounds (x + 3, y + offset, 10, 10);
  586. DeviceContext.DrawRectangle (new Pen (Color.Black, 2), x + 0.5F + 3, y + 0.5F + offset, 11, 11);
  587. if (node.Checked) {
  588. Pen check_pen = new Pen (Color.Black, 1);
  589. DeviceContext.DrawLine (check_pen, x + 6, y + offset + 5, x + 8, y + offset + 8);
  590. DeviceContext.DrawLine (check_pen, x + 6, y + offset + 6, x + 8, y + offset + 9);
  591. DeviceContext.DrawLine (check_pen, x + 7, y + offset + 8, x + 13, y + offset + 3);
  592. DeviceContext.DrawLine (check_pen, x + 7, y + offset + 9, x + 13, y + offset + 4);
  593. }
  594. }
  595. private void DrawNodeLines (TreeNode node, Pen dash, int x, int y, int middle, int item_height, int node_count)
  596. {
  597. int ladjust = 9; // left adjust
  598. int radjust = 0; // right adjust
  599. if (node_count > 0 && show_plus_minus)
  600. ladjust = 13;
  601. if (checkboxes)
  602. radjust = 3;
  603. DeviceContext.DrawLine (dash, x - indent + ladjust, middle, x + radjust, middle);
  604. int ly = 0;
  605. if (node.PrevNode != null) {
  606. int prevadjust = (node.Nodes.Count > 0 && show_plus_minus ? (node.PrevNode.Nodes.Count == 0 ? 0 : 4) :
  607. (node.PrevNode.Nodes.Count == 0 ? 0 : 4));
  608. int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
  609. ly = node.PrevNode.Bounds.Bottom - (item_height / 2) + prevadjust;
  610. DeviceContext.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
  611. } else if (node.Parent != null) {
  612. int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
  613. ly = node.Parent.Bounds.Bottom - 1;
  614. DeviceContext.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
  615. }
  616. }
  617. private void DrawNodeImage (TreeNode node, int x, int y)
  618. {
  619. if (node.ImageIndex > -1 && ImageList != null && node.ImageIndex < ImageList.Images.Count) {
  620. ImageList.Draw (DeviceContext, x, y + 2, ImageList.ImageSize.Width,
  621. ImageList.ImageSize.Height, node.ImageIndex);
  622. } else if (ImageIndex > -1 && ImageList != null && ImageIndex < ImageList.Images.Count) {
  623. ImageList.Draw (DeviceContext, x, y + 2, ImageList.ImageSize.Width,
  624. ImageList.ImageSize.Height, ImageIndex);
  625. }
  626. }
  627. private void UpdateNodeBounds (TreeNode node, int x, int y, int item_height)
  628. {
  629. int width = (int) (node.Text.Length * Font.Size);
  630. int xoff = indent;
  631. if (!show_root_lines && node.Parent == null)
  632. xoff = 0;
  633. if (image_list != null)
  634. xoff += image_list.ImageSize.Width;
  635. node.UpdateBounds (x + xoff, y, width, item_height);
  636. }
  637. private void DrawNode (TreeNode node, Rectangle clip, ref int depth, ref int node_count, int item_height,
  638. Font font, ref int visible_node_count, int max_height)
  639. {
  640. node_count++;
  641. int x = (!show_root_lines && node.Parent != null ? depth - 1 : depth) * indent - hbar_offset;
  642. int y = item_height * (node_count - skipped_nodes - 1);
  643. bool visible = (y >= 0 && y < max_height);
  644. if (visible)
  645. visible_node_count++;
  646. // The thing is totally out of the clipping rectangle
  647. if (clip.Top > y + ItemHeight || clip.Bottom < y)
  648. visible = false;
  649. int _n_count = node.nodes.Count;
  650. int middle = y + (item_height / 2);
  651. if (show_root_lines || node.Parent != null) {
  652. x += 5;
  653. if (_n_count > 0) {
  654. if (show_plus_minus && visible) {
  655. DrawNodePlusMinus (node, x, y, middle);
  656. }
  657. }
  658. x += indent - 5;
  659. }
  660. int ox = x;
  661. if (checkboxes) {
  662. DrawNodeCheckBox (node, ox, y);
  663. ox += 19;
  664. }
  665. if (show_lines)
  666. DrawNodeLines (node, dash, x, y, middle, item_height, _n_count);
  667. if (ImageList != null) {
  668. if (visible)
  669. DrawNodeImage (node, ox, y);
  670. // MS leaves the space for the image if the ImageList is
  671. // non null regardless of whether or not an image is drawn
  672. ox += ImageList.ImageSize.Width + 3; // leave a little space so the text isn't against the image
  673. }
  674. UpdateNodeBounds (node, x, y, item_height);
  675. if (visible) {
  676. Rectangle r = node.Bounds;
  677. StringFormat format = new StringFormat ();
  678. format.LineAlignment = StringAlignment.Center;
  679. r.Y += 2; // we have to adjust this to get nice middle alignment
  680. r.X = ox;
  681. Color text_color = (Focused && SelectedNode == node ? ThemeEngine.Current.ColorHilightText : node.ForeColor);
  682. if (Focused) {
  683. if (SelectedNode == node)
  684. DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorHilight), node.Bounds);
  685. if (focused_node == node) {
  686. Pen dot_pen = new Pen (ThemeEngine.Current.ColorButtonHilight, 1);
  687. dot_pen.DashStyle = DashStyle.Dot;
  688. DeviceContext.DrawRectangle (new Pen (ThemeEngine.Current.ColorButtonDkShadow),
  689. node.Bounds.X, node.Bounds.Y, node.Bounds.Width - 1, node.Bounds.Height - 1);
  690. DeviceContext.DrawRectangle (dot_pen, node.Bounds.X, node.Bounds.Y, node.Bounds.Width - 1, node.Bounds.Height - 1);
  691. }
  692. } else {
  693. if (!HideSelection && SelectedNode == node)
  694. DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorButtonFace), node.Bounds);
  695. }
  696. DeviceContext.DrawString (node.Text, font, new SolidBrush (text_color), r, format);
  697. y += item_height + 1;
  698. }
  699. if (node.Bounds.Right > max_node_width)
  700. max_node_width = node.Bounds.Right;
  701. depth++;
  702. if (node.IsExpanded) {
  703. for (int i = 0; i < _n_count; i++) {
  704. int tdepth = depth;
  705. DrawNode (node.nodes [i], clip, ref tdepth, ref node_count, item_height,
  706. font, ref visible_node_count, max_height);
  707. }
  708. }
  709. }
  710. private void AddVerticalScrollBar (int total_nodes, bool count_changed)
  711. {
  712. if (vbar == null) {
  713. vbar = new VScrollBar ();
  714. count_changed = true;
  715. }
  716. vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width,
  717. 0, vbar.Width, (add_hscroll ? Height - ThemeEngine.Current.HScrollBarDefaultSize.Height : Height));
  718. if (count_changed) {
  719. vbar.Maximum = total_nodes;
  720. int height = ClientRectangle.Height;
  721. vbar.LargeChange = height / ItemHeight;
  722. }
  723. if (!vbar_added) {
  724. Controls.Add (vbar);
  725. vbar.ValueChanged += new EventHandler (VScrollBarValueChanged);
  726. vbar_added = true;
  727. }
  728. vbar.Visible = true;
  729. }
  730. private void AddHorizontalScrollBar ()
  731. {
  732. if (hbar == null)
  733. hbar = new HScrollBar ();
  734. hbar.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hbar.Height,
  735. (add_vscroll ? Width - ThemeEngine.Current.VScrollBarDefaultSize.Width : Width), hbar.Height);
  736. if (!hbar_added) {
  737. Controls.Add (hbar);
  738. hbar.ValueChanged += new EventHandler (HScrollBarValueChanged);
  739. hbar_added = true;
  740. }
  741. hbar.Visible = true;
  742. }
  743. private void AddSizeGrip ()
  744. {
  745. if (grip == null)
  746. grip = new SizeGrip ();
  747. grip.Bounds = new Rectangle (hbar.Right, vbar.Bottom, vbar.Width, hbar.Height);
  748. if (!grip_added) {
  749. grip.BackColor = ThemeEngine.Current.ColorButtonFace;
  750. Controls.Add (grip);
  751. grip_added = true;
  752. }
  753. grip.Visible = true;
  754. }
  755. private void SizeChangedHandler (object sender, EventArgs e)
  756. {
  757. if (max_node_width > ClientRectangle.Width) {
  758. add_hscroll = true;
  759. AddHorizontalScrollBar ();
  760. }
  761. if (vbar != null) {
  762. vbar.Left = Right - vbar.Width;
  763. vbar.Height = Height;
  764. }
  765. if (hbar != null) {
  766. hbar.Top = Bottom - hbar.Height;
  767. hbar.Width = Width;
  768. }
  769. if (grip != null) {
  770. grip.Top = Bottom - grip.Height;
  771. }
  772. }
  773. private void VScrollBarValueChanged (object sender, EventArgs e)
  774. {
  775. int old_skip = skipped_nodes;
  776. skipped_nodes = vbar.Value;
  777. int y_move = (old_skip - skipped_nodes) * ItemHeight;
  778. XplatUI.ScrollWindow (Handle, ViewportRectangle, 0, y_move, true);
  779. }
  780. private void HScrollBarValueChanged(object sender, EventArgs e)
  781. {
  782. int old_offset = hbar_offset;
  783. hbar_offset = hbar.Value;
  784. XplatUI.ScrollWindow (Handle, ViewportRectangle, old_offset - hbar_offset, 0, true);
  785. }
  786. private int GetOpenNodeCount ()
  787. {
  788. if (Nodes.Count < 1)
  789. return 0;
  790. OpenTreeNodeEnumerator e = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
  791. int count = 0;
  792. while (e.MoveNext ()) {
  793. count++;
  794. }
  795. return count;
  796. }
  797. // TODO: Handle all sorts o stuff here
  798. private void MouseDownHandler (object sender, MouseEventArgs e)
  799. {
  800. if (!show_plus_minus)
  801. return;
  802. TreeNode node = GetNodeAt (e.Y);
  803. if (node == null)
  804. return;
  805. if (IsTextArea (node, e.X)) {
  806. selected_node = node;
  807. Console.WriteLine ("selected node");
  808. if (selected_node != focused_node) {
  809. select_mmove = true;
  810. Refresh ();
  811. }
  812. } else if (node.PlusMinusBounds.Contains (e.X, e.Y)) {
  813. node.Toggle ();
  814. return;
  815. } else if (node.CheckBoxBounds.Contains (e.X, e.Y)) {
  816. node.Checked = !node.Checked;
  817. return;
  818. }
  819. }
  820. private void MouseUpHandler (object sender, MouseEventArgs e) {
  821. if (!select_mmove)
  822. return;
  823. select_mmove = false;
  824. TreeViewCancelEventArgs ce = new TreeViewCancelEventArgs (selected_node, false, TreeViewAction.ByMouse);
  825. OnBeforeSelect (ce);
  826. if (!ce.Cancel) {
  827. focused_node = selected_node;
  828. OnAfterSelect (new TreeViewEventArgs (selected_node, TreeViewAction.ByMouse));
  829. } else {
  830. selected_node = focused_node;
  831. }
  832. Refresh();
  833. }
  834. private void MouseMoveHandler (object sender, MouseEventArgs e) {
  835. if(!select_mmove)
  836. return;
  837. TreeNode node = GetNodeAt(e.X,e.Y);
  838. if(node == selected_node)
  839. return;
  840. selected_node = focused_node;
  841. select_mmove = false;
  842. Refresh();
  843. }
  844. private void DoubleClickHandler (object sender, MouseEventArgs e) {
  845. TreeNode node = GetNodeAt(e.X,e.Y);
  846. if(node != null) {
  847. node.Toggle();
  848. }
  849. }
  850. public event TreeViewEventHandler AfterCheck {
  851. add { on_after_check += value; }
  852. remove { on_after_check -= value; }
  853. }
  854. public event TreeViewEventHandler AfterCollapse {
  855. add { on_after_collapse += value; }
  856. remove { on_after_collapse -= value; }
  857. }
  858. public event TreeViewEventHandler AfterExpand {
  859. add { on_after_expand += value; }
  860. remove { on_after_expand -= value; }
  861. }
  862. public event NodeLabelEditEventHandler AfterLabelEdit {
  863. add { on_after_label_edit += value; }
  864. remove { on_after_label_edit -= value; }
  865. }
  866. public event TreeViewEventHandler AfterSelect {
  867. add { on_after_select += value; }
  868. remove { on_after_select -= value; }
  869. }
  870. public event TreeViewCancelEventHandler BeforeCheck {
  871. add { on_before_check += value; }
  872. remove { on_before_check -= value; }
  873. }
  874. public event TreeViewCancelEventHandler BeforeCollapse {
  875. add { on_before_collapse += value; }
  876. remove { on_before_collapse -= value; }
  877. }
  878. public event TreeViewCancelEventHandler BeforeExpand {
  879. add { on_before_expand += value; }
  880. remove { on_before_expand -= value; }
  881. }
  882. public event NodeLabelEditEventHandler BeforeLabelEdit {
  883. add { on_before_label_edit += value; }
  884. remove { on_before_label_edit -= value; }
  885. }
  886. public event TreeViewCancelEventHandler BeforeSelect {
  887. add { on_before_select += value; }
  888. remove { on_before_select -= value; }
  889. }
  890. public new event PaintEventHandler Paint {
  891. add { base.Paint += value; }
  892. remove { base.Paint -= value; }
  893. }
  894. public new event EventHandler TextChanged {
  895. add { base.TextChanged += value; }
  896. remove { base.TextChanged -= value; }
  897. }
  898. }
  899. }