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. if (top_node == null && Nodes.Count > 0)
  509. top_node = nodes [0];
  510. // Decide if we need a scrollbar
  511. int old_open_node_count = open_node_count;
  512. Rectangle fill = ClientRectangle;
  513. add_vscroll = false;
  514. add_hscroll = false;
  515. DeviceContext.FillRectangle (new SolidBrush (BackColor), fill);
  516. int depth = 0;
  517. int item_height = ItemHeight;
  518. Font font = Font;
  519. int height = ClientRectangle.Height;
  520. open_node_count = 0;
  521. foreach (TreeNode node in nodes) {
  522. DrawNode (node, clip, ref depth, item_height, font, height);
  523. depth = 0;
  524. }
  525. add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height;
  526. if (max_node_width > ClientRectangle.Width)
  527. add_hscroll = true;
  528. if (add_vscroll)
  529. add_hscroll = max_node_width > ClientRectangle.Width - ThemeEngine.Current.VScrollBarDefaultSize.Width;
  530. if (add_hscroll)
  531. add_vscroll = (open_node_count * ItemHeight) > ClientRectangle.Height - ThemeEngine.Current.HScrollBarDefaultSize.Width;
  532. if (add_hscroll) {
  533. AddHorizontalScrollBar ();
  534. } else if (hbar != null) {
  535. hbar_offset = 0;
  536. hbar.Visible = false;
  537. }
  538. if (add_vscroll) {
  539. AddVerticalScrollBar (open_node_count, old_open_node_count != open_node_count);
  540. } else if (vbar != null) {
  541. vbar.Visible = false;
  542. skipped_nodes = 0;
  543. }
  544. if (add_hscroll && add_vscroll)
  545. AddSizeGrip ();
  546. else if (grip != null)
  547. grip.Visible = false;
  548. }
  549. private void DrawNodePlusMinus (TreeNode node, Rectangle clip, int x, int y, int middle)
  550. {
  551. node.UpdatePlusMinusBounds (x, middle - 4, 8, 8);
  552. if (!clip.IntersectsWith (node.PlusMinusBounds))
  553. return;
  554. DeviceContext.DrawRectangle (SystemPens.ControlDark, node.PlusMinusBounds);
  555. if (node.IsExpanded) {
  556. DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
  557. } else {
  558. DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 2, middle, x + 6, middle);
  559. DeviceContext.DrawLine (SystemPens.ControlDarkDark, x + 4, middle - 2, x + 4, middle + 2);
  560. }
  561. }
  562. private void DrawNodeCheckBox (TreeNode node, Rectangle clip, int x, int y)
  563. {
  564. int offset = (ItemHeight - 13);
  565. node.UpdateCheckBoxBounds (x + 3, y + offset, 10, 10);
  566. // new rectangle that factors in line width
  567. if (!RectsIntersect (clip, x + 3, y + offset, 12, 12))
  568. return;
  569. DeviceContext.DrawRectangle (new Pen (Color.Black, 2), x + 0.5F + 3, y + 0.5F + offset, 11, 11);
  570. if (node.Checked) {
  571. Pen check_pen = new Pen (Color.Black, 1);
  572. DeviceContext.DrawLine (check_pen, x + 6, y + offset + 5, x + 8, y + offset + 8);
  573. DeviceContext.DrawLine (check_pen, x + 6, y + offset + 6, x + 8, y + offset + 9);
  574. DeviceContext.DrawLine (check_pen, x + 7, y + offset + 8, x + 13, y + offset + 3);
  575. DeviceContext.DrawLine (check_pen, x + 7, y + offset + 9, x + 13, y + offset + 4);
  576. }
  577. }
  578. private void DrawNodeLines (TreeNode node, bool visible, Pen dash, int x, int y, int middle, int item_height, int node_count)
  579. {
  580. int ladjust = 9; // left adjust
  581. int radjust = 0; // right adjust
  582. if (node_count > 0 && show_plus_minus)
  583. ladjust = 13;
  584. if (checkboxes)
  585. radjust = 3;
  586. DeviceContext.DrawLine (dash, x - indent + ladjust, middle, x + radjust, middle);
  587. if (!visible)
  588. return;
  589. int ly = 0;
  590. if (node.PrevNode != null) {
  591. int prevadjust = (node.Nodes.Count > 0 && show_plus_minus ? (node.PrevNode.Nodes.Count == 0 ? 0 : 4) :
  592. (node.PrevNode.Nodes.Count == 0 ? 0 : 4));
  593. int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
  594. ly = node.PrevNode.Bounds.Bottom - (item_height / 2) + prevadjust;
  595. DeviceContext.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
  596. } else if (node.Parent != null) {
  597. int myadjust = (node.Nodes.Count > 0 && show_plus_minus ? 4 : 0);
  598. ly = node.Parent.Bounds.Bottom - 1;
  599. DeviceContext.DrawLine (dash, x - indent + 9, middle - myadjust, x - indent + 9, ly);
  600. }
  601. }
  602. private void DrawNodeImage (TreeNode node, Rectangle clip, int x, int y)
  603. {
  604. Rectangle r = new Rectangle (x, y + 2, ImageList.ImageSize.Width,
  605. ImageList.ImageSize.Height);
  606. if (!RectsIntersect (r, x, y + 2, ImageList.ImageSize.Width, ImageList.ImageSize.Height))
  607. return;
  608. if (node.ImageIndex > -1 && ImageList != null && node.ImageIndex < ImageList.Images.Count) {
  609. ImageList.Draw (DeviceContext, x, y + 2, ImageList.ImageSize.Width,
  610. ImageList.ImageSize.Height, node.ImageIndex);
  611. } else if (ImageIndex > -1 && ImageList != null && ImageIndex < ImageList.Images.Count) {
  612. ImageList.Draw (DeviceContext, x, y + 2, ImageList.ImageSize.Width,
  613. ImageList.ImageSize.Height, ImageIndex);
  614. }
  615. }
  616. private void UpdateNodeBounds (TreeNode node, int x, int y, int item_height)
  617. {
  618. int width = (int) (node.Text.Length * Font.Size);
  619. int xoff = indent;
  620. if (!show_root_lines && node.Parent == null)
  621. xoff = 0;
  622. if (image_list != null)
  623. xoff += image_list.ImageSize.Width;
  624. node.UpdateBounds (x + xoff, y, width, item_height);
  625. }
  626. private void DrawNode (TreeNode node, Rectangle clip, ref int depth, int item_height,
  627. Font font, int max_height)
  628. {
  629. open_node_count++;
  630. int x = (!show_root_lines && node.Parent != null ? depth - 1 : depth) * indent - hbar_offset;
  631. int y = item_height * (open_node_count - skipped_nodes - 1);
  632. bool visible = (y >= 0 && y < max_height);
  633. // The thing is totally out of the clipping rectangle
  634. if (clip.Top > y + ItemHeight || clip.Bottom < y)
  635. visible = false;
  636. int _n_count = node.nodes.Count;
  637. int middle = y + (item_height / 2);
  638. if (show_root_lines || node.Parent != null) {
  639. x += 5;
  640. if (_n_count > 0) {
  641. if (show_plus_minus && visible) {
  642. DrawNodePlusMinus (node, clip, x, y, middle);
  643. }
  644. }
  645. x += indent - 5;
  646. }
  647. int ox = x;
  648. if (visible && checkboxes) {
  649. DrawNodeCheckBox (node, clip, ox, y);
  650. ox += 19;
  651. }
  652. if (show_lines)
  653. DrawNodeLines (node, visible, dash, x, y, middle, item_height, _n_count);
  654. if (visible && ImageList != null) {
  655. if (visible)
  656. DrawNodeImage (node, clip, ox, y);
  657. // MS leaves the space for the image if the ImageList is
  658. // non null regardless of whether or not an image is drawn
  659. ox += ImageList.ImageSize.Width + 3; // leave a little space so the text isn't against the image
  660. }
  661. UpdateNodeBounds (node, x, y, item_height);
  662. if (visible && clip.IntersectsWith (node.Bounds)) {
  663. Rectangle r = node.Bounds;
  664. StringFormat format = new StringFormat ();
  665. format.LineAlignment = StringAlignment.Center;
  666. r.Y += 2; // we have to adjust this to get nice middle alignment
  667. r.X = ox;
  668. Color text_color = (Focused && SelectedNode == node ? ThemeEngine.Current.ColorHilightText : node.ForeColor);
  669. if (Focused) {
  670. if (SelectedNode == node)
  671. DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorHilight), node.Bounds);
  672. if (focused_node == node) {
  673. Pen dot_pen = new Pen (ThemeEngine.Current.ColorButtonHilight, 1);
  674. dot_pen.DashStyle = DashStyle.Dot;
  675. DeviceContext.DrawRectangle (new Pen (ThemeEngine.Current.ColorButtonDkShadow),
  676. node.Bounds.X, node.Bounds.Y, node.Bounds.Width - 1, node.Bounds.Height - 1);
  677. DeviceContext.DrawRectangle (dot_pen, node.Bounds.X, node.Bounds.Y, node.Bounds.Width - 1, node.Bounds.Height - 1);
  678. }
  679. } else {
  680. if (!HideSelection && SelectedNode == node)
  681. DeviceContext.FillRectangle (new SolidBrush (ThemeEngine.Current.ColorButtonFace), node.Bounds);
  682. }
  683. DeviceContext.DrawString (node.Text, font, new SolidBrush (text_color), r, format);
  684. y += item_height + 1;
  685. }
  686. if (node.Bounds.Right > max_node_width) {
  687. max_node_width = node.Bounds.Right;
  688. if (max_node_width > ClientRectangle.Width && !add_hscroll) {
  689. max_height -= ItemHeight;
  690. add_hscroll = true;
  691. }
  692. }
  693. depth++;
  694. if (node.IsExpanded) {
  695. for (int i = 0; i < _n_count; i++) {
  696. int tdepth = depth;
  697. DrawNode (node.nodes [i], clip, ref tdepth, item_height, font, max_height);
  698. }
  699. }
  700. }
  701. private void AddVerticalScrollBar (int total_nodes, bool count_changed)
  702. {
  703. if (vbar == null) {
  704. vbar = new VScrollBar ();
  705. count_changed = true;
  706. }
  707. vbar.Bounds = new Rectangle (ClientRectangle.Width - vbar.Width,
  708. 0, vbar.Width, (add_hscroll ? Height - ThemeEngine.Current.HScrollBarDefaultSize.Height : Height));
  709. if (count_changed) {
  710. vbar.Maximum = total_nodes;
  711. int height = ClientRectangle.Height;
  712. vbar.LargeChange = height / ItemHeight;
  713. }
  714. if (!vbar_added) {
  715. Controls.Add (vbar);
  716. vbar.ValueChanged += new EventHandler (VScrollBarValueChanged);
  717. vbar_added = true;
  718. }
  719. vbar.Visible = true;
  720. }
  721. private void AddHorizontalScrollBar ()
  722. {
  723. if (hbar == null)
  724. hbar = new HScrollBar ();
  725. hbar.Bounds = new Rectangle (ClientRectangle.Left, ClientRectangle.Bottom - hbar.Height,
  726. (add_vscroll ? Width - ThemeEngine.Current.VScrollBarDefaultSize.Width : Width), hbar.Height);
  727. if (!hbar_added) {
  728. Controls.Add (hbar);
  729. hbar.ValueChanged += new EventHandler (HScrollBarValueChanged);
  730. hbar_added = true;
  731. }
  732. hbar.Visible = true;
  733. }
  734. private void AddSizeGrip ()
  735. {
  736. if (grip == null)
  737. grip = new SizeGrip ();
  738. grip.Bounds = new Rectangle (hbar.Right, vbar.Bottom, vbar.Width, hbar.Height);
  739. if (!grip_added) {
  740. grip.BackColor = ThemeEngine.Current.ColorButtonFace;
  741. Controls.Add (grip);
  742. grip_added = true;
  743. }
  744. grip.Visible = true;
  745. }
  746. private void SizeChangedHandler (object sender, EventArgs e)
  747. {
  748. SuspendLayout ();
  749. if (max_node_width > ClientRectangle.Width) {
  750. add_hscroll = true;
  751. AddHorizontalScrollBar ();
  752. }
  753. if (vbar != null) {
  754. int height = (hbar != null && hbar.Visible ? Height - hbar.Height : Height);
  755. vbar.SetBounds (Right - vbar.Width, 0, 0, height, BoundsSpecified.X | BoundsSpecified.Height);
  756. }
  757. if (hbar != null) {
  758. int width = (vbar != null && vbar.Visible ? Width - vbar.Width : Width);
  759. hbar.SetBounds (0, Bottom - hbar.Height, width, 0, BoundsSpecified.Y | BoundsSpecified.Width);
  760. }
  761. if (grip != null) {
  762. grip.SetBounds (Right - grip.Width, Bottom - grip.Height, 0, 0, BoundsSpecified.X | BoundsSpecified.Y);
  763. }
  764. ResumeLayout ();
  765. }
  766. private void VScrollBarValueChanged (object sender, EventArgs e)
  767. {
  768. int old_skip = skipped_nodes;
  769. skipped_nodes = vbar.Value;
  770. int y_move = (old_skip - skipped_nodes) * ItemHeight;
  771. XplatUI.ScrollWindow (Handle, ViewportRectangle, 0, y_move, true);
  772. }
  773. private void HScrollBarValueChanged(object sender, EventArgs e)
  774. {
  775. int old_offset = hbar_offset;
  776. hbar_offset = hbar.Value;
  777. XplatUI.ScrollWindow (Handle, ViewportRectangle, old_offset - hbar_offset, 0, true);
  778. }
  779. private int GetOpenNodeCount ()
  780. {
  781. if (Nodes.Count < 1)
  782. return 0;
  783. OpenTreeNodeEnumerator e = new OpenTreeNodeEnumerator (root_node.Nodes [0]);
  784. int count = 0;
  785. while (e.MoveNext ()) {
  786. count++;
  787. }
  788. return count;
  789. }
  790. // TODO: Handle all sorts o stuff here
  791. private void MouseDownHandler (object sender, MouseEventArgs e)
  792. {
  793. if (!show_plus_minus)
  794. return;
  795. TreeNode node = GetNodeAt (e.Y);
  796. if (node == null)
  797. return;
  798. if (IsTextArea (node, e.X)) {
  799. selected_node = node;
  800. Console.WriteLine ("selected node");
  801. if (selected_node != focused_node) {
  802. select_mmove = true;
  803. Refresh ();
  804. }
  805. } else if (node.PlusMinusBounds.Contains (e.X, e.Y)) {
  806. node.Toggle ();
  807. return;
  808. } else if (node.CheckBoxBounds.Contains (e.X, e.Y)) {
  809. node.Checked = !node.Checked;
  810. return;
  811. }
  812. }
  813. private void MouseUpHandler (object sender, MouseEventArgs e) {
  814. if (!select_mmove)
  815. return;
  816. select_mmove = false;
  817. TreeViewCancelEventArgs ce = new TreeViewCancelEventArgs (selected_node, false, TreeViewAction.ByMouse);
  818. OnBeforeSelect (ce);
  819. if (!ce.Cancel) {
  820. focused_node = selected_node;
  821. OnAfterSelect (new TreeViewEventArgs (selected_node, TreeViewAction.ByMouse));
  822. } else {
  823. selected_node = focused_node;
  824. }
  825. Refresh();
  826. }
  827. private void MouseMoveHandler (object sender, MouseEventArgs e) {
  828. if(!select_mmove)
  829. return;
  830. TreeNode node = GetNodeAt(e.X,e.Y);
  831. if(node == selected_node)
  832. return;
  833. selected_node = focused_node;
  834. select_mmove = false;
  835. Refresh();
  836. }
  837. private void DoubleClickHandler (object sender, MouseEventArgs e) {
  838. TreeNode node = GetNodeAt(e.X,e.Y);
  839. if(node != null) {
  840. node.Toggle();
  841. }
  842. }
  843. private bool RectsIntersect (Rectangle r, int left, int top, int width, int height)
  844. {
  845. return !((r.Left > left + width) || (r.Right < left) ||
  846. (r.Top > top + height) || (r.Bottom < top));
  847. }
  848. public event TreeViewEventHandler AfterCheck {
  849. add { on_after_check += value; }
  850. remove { on_after_check -= value; }
  851. }
  852. public event TreeViewEventHandler AfterCollapse {
  853. add { on_after_collapse += value; }
  854. remove { on_after_collapse -= value; }
  855. }
  856. public event TreeViewEventHandler AfterExpand {
  857. add { on_after_expand += value; }
  858. remove { on_after_expand -= value; }
  859. }
  860. public event NodeLabelEditEventHandler AfterLabelEdit {
  861. add { on_after_label_edit += value; }
  862. remove { on_after_label_edit -= value; }
  863. }
  864. public event TreeViewEventHandler AfterSelect {
  865. add { on_after_select += value; }
  866. remove { on_after_select -= value; }
  867. }
  868. public event TreeViewCancelEventHandler BeforeCheck {
  869. add { on_before_check += value; }
  870. remove { on_before_check -= value; }
  871. }
  872. public event TreeViewCancelEventHandler BeforeCollapse {
  873. add { on_before_collapse += value; }
  874. remove { on_before_collapse -= value; }
  875. }
  876. public event TreeViewCancelEventHandler BeforeExpand {
  877. add { on_before_expand += value; }
  878. remove { on_before_expand -= value; }
  879. }
  880. public event NodeLabelEditEventHandler BeforeLabelEdit {
  881. add { on_before_label_edit += value; }
  882. remove { on_before_label_edit -= value; }
  883. }
  884. public event TreeViewCancelEventHandler BeforeSelect {
  885. add { on_before_select += value; }
  886. remove { on_before_select -= value; }
  887. }
  888. public new event PaintEventHandler Paint {
  889. add { base.Paint += value; }
  890. remove { base.Paint -= value; }
  891. }
  892. public new event EventHandler TextChanged {
  893. add { base.TextChanged += value; }
  894. remove { base.TextChanged -= value; }
  895. }
  896. }
  897. }