2
0

TreeView.cs 32 KB

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