TreeView.cs 28 KB

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