New tests.
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / TreeView.cs
1 //
2 // System.Web.UI.WebControls.TreeView.cs
3 //
4 // Authors:
5 //      Lluis Sanchez Gual (lluis@novell.com)
6 //
7 // (C) 2004 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
29 //
30
31 #if NET_2_0
32
33 using System.Collections;
34 using System.Text;
35 using System.ComponentModel;
36 using System.Globalization;
37 using System.Web.Handlers;
38 using System.Collections.Specialized;
39 using System.IO;
40 using System.Security.Permissions;
41 using System.Collections.Generic;
42 using System.Web.Util;
43
44 namespace System.Web.UI.WebControls
45 {
46         // CAS
47         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
49         // attributes
50         [SupportsEventValidation]
51         [ControlValueProperty ("SelectedValue")]
52         [DefaultEvent ("SelectedNodeChanged")]
53         [Designer ("System.Web.UI.Design.WebControls.TreeViewDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
54         public class TreeView: HierarchicalDataBoundControl, IPostBackEventHandler, IPostBackDataHandler, ICallbackEventHandler
55         {
56                 string activeSiteMapPath;
57                 bool stylesPrepared;
58                 Style hoverNodeStyle;
59                 TreeNodeStyle leafNodeStyle;
60                 TreeNodeStyle nodeStyle;
61                 TreeNodeStyle parentNodeStyle;
62                 TreeNodeStyle rootNodeStyle;
63                 TreeNodeStyle selectedNodeStyle;
64                 
65                 TreeNodeStyleCollection levelStyles;
66                 TreeNodeCollection nodes;
67                 TreeNodeBindingCollection dataBindings;
68                 
69                 TreeNode selectedNode;
70                 Hashtable bindings;
71
72                 int registeredStylesCounter = -1;
73                 List<Style> levelLinkStyles;
74                 Style controlLinkStyle;
75                 Style nodeLinkStyle;
76                 Style rootNodeLinkStyle;
77                 Style parentNodeLinkStyle;
78                 Style leafNodeLinkStyle;
79                 Style selectedNodeLinkStyle;
80                 Style hoverNodeLinkStyle;
81                 
82                 static readonly object TreeNodeCheckChangedEvent = new object();
83                 static readonly object SelectedNodeChangedEvent = new object();
84                 static readonly object TreeNodeCollapsedEvent = new object();
85                 static readonly object TreeNodeDataBoundEvent = new object();
86                 static readonly object TreeNodeExpandedEvent = new object();
87                 static readonly object TreeNodePopulateEvent = new object();
88                 
89                 static Hashtable imageStyles = new Hashtable ();
90
91                 class TreeViewExpandDepthConverter : TypeConverter
92                 {
93                         public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
94                         {
95                                 if (sourceType == typeof (string) || sourceType == typeof (int))
96                                         return true;
97
98                                 return base.CanConvertFrom (context, sourceType);
99                         }
100
101                         public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
102                         {
103                                 if (destinationType == typeof (string) || destinationType == typeof (int))
104                                         return true;
105
106                                 return base.CanConvertTo (context, destinationType);
107                         }
108
109                         public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
110                         {
111                                 if (destinationType != typeof (int) && destinationType != typeof (string))
112                                         return base.ConvertTo (context, culture, value, destinationType);
113                                 
114                                 if (value is string) {
115                                         if (destinationType == typeof (int)) {
116                                                 if (String.Compare ("FullyExpand", (string)value, StringComparison.OrdinalIgnoreCase) == 0)
117                                                         return -1;
118                                                 
119                                                 try {
120                                                         return Int32.Parse ((string)value);
121                                                 } catch (Exception) {
122                                                         return -1;
123                                                 }
124                                         } else
125                                                 return value;   
126                                 }
127
128                                 int val = (int)value;
129                                 if (destinationType == typeof (string)) {
130                                         if (val == -1)
131                                                 return "FullyExpand";
132                                         return val.ToString ();
133                                 }
134                                         
135                                 return value;
136                         }
137
138                         public override object ConvertFrom (ITypeDescriptorContext context, CultureInfo culture, object value)
139                         {
140                                 if (!(value is string) && !(value is int))
141                                         return base.ConvertFrom (context, culture, value);
142
143                                 if (value is string) {
144                                         if (String.Compare ("FullyExpand", (string)value, StringComparison.OrdinalIgnoreCase) == 0)
145                                                 return -1;
146
147                                         try {
148                                                 return Int32.Parse ((string)value);
149                                         } catch (Exception) {
150                                                 return null;
151                                         }
152                                 }
153
154                                 return value;
155                         }
156                 }
157                 
158                 class ImageStyle
159                 {
160                         public ImageStyle (string expand, string collapse, string noExpand, string icon, string iconLeaf, string iconRoot)
161                         {
162                                 Expand = expand;
163                                 Collapse = collapse;
164                                 NoExpand = noExpand;
165                                 RootIcon = iconRoot;
166                                 ParentIcon = icon;
167                                 LeafIcon = iconLeaf;
168                         }
169                         
170                         public string Expand;
171                         public string Collapse;
172                         public string NoExpand;
173                         public string RootIcon;
174                         public string ParentIcon;
175                         public string LeafIcon;
176                 }
177                 
178                 static TreeView ()
179                 {
180                         imageStyles [TreeViewImageSet.Arrows] = new ImageStyle ("arrow_plus", "arrow_minus", "arrow_noexpand", null, null, null);
181                         imageStyles [TreeViewImageSet.BulletedList] = new ImageStyle (null, null, null, "dot_full", "dot_empty", "dot_full");
182                         imageStyles [TreeViewImageSet.BulletedList2] = new ImageStyle (null, null, null, "box_full", "box_empty", "box_full");
183                         imageStyles [TreeViewImageSet.BulletedList3] = new ImageStyle (null, null, null, "star_full", "star_empty", "star_full");
184                         imageStyles [TreeViewImageSet.BulletedList4] = new ImageStyle (null, null, null, "star_full", "star_empty", "dots");
185                         imageStyles [TreeViewImageSet.Contacts] = new ImageStyle ("TreeView_plus", "TreeView_minus", "contact", null, null, null);
186                         imageStyles [TreeViewImageSet.Events] = new ImageStyle (null, null, null, "warning", "warning", "warning");
187                         imageStyles [TreeViewImageSet.Inbox] = new ImageStyle (null, null, null, "inbox", "inbox", "inbox");
188                         imageStyles [TreeViewImageSet.Msdn] = new ImageStyle ("box_plus", "box_minus", "box_noexpand", null, null, null);
189                         imageStyles [TreeViewImageSet.Simple] = new ImageStyle (null, null, "box_full", null, null, null);
190                         imageStyles [TreeViewImageSet.Simple2] = new ImageStyle (null, null, "box_empty", null, null, null);
191
192                         // TODO
193                         imageStyles [TreeViewImageSet.News] = new ImageStyle ("TreeView_plus", "TreeView_minus", "TreeView_noexpand", null, null, null);
194                         imageStyles [TreeViewImageSet.Faq] = new ImageStyle ("TreeView_plus", "TreeView_minus", "TreeView_noexpand", null, null, null);
195                         imageStyles [TreeViewImageSet.WindowsHelp] = new ImageStyle ("TreeView_plus", "TreeView_minus", "TreeView_noexpand", null, null, null);
196                         imageStyles [TreeViewImageSet.XPFileExplorer] = new ImageStyle ("TreeView_plus", "TreeView_minus", "TreeView_noexpand", "folder", "file", "computer");
197                 }
198                 
199                 public event TreeNodeEventHandler TreeNodeCheckChanged {
200                         add { Events.AddHandler (TreeNodeCheckChangedEvent, value); }
201                         remove { Events.RemoveHandler (TreeNodeCheckChangedEvent, value); }
202                 }
203                 
204                 public event EventHandler SelectedNodeChanged {
205                         add { Events.AddHandler (SelectedNodeChangedEvent, value); }
206                         remove { Events.RemoveHandler (SelectedNodeChangedEvent, value); }
207                 }
208                 
209                 public event TreeNodeEventHandler TreeNodeCollapsed {
210                         add { Events.AddHandler (TreeNodeCollapsedEvent, value); }
211                         remove { Events.RemoveHandler (TreeNodeCollapsedEvent, value); }
212                 }
213                 
214                 public event TreeNodeEventHandler TreeNodeDataBound {
215                         add { Events.AddHandler (TreeNodeDataBoundEvent, value); }
216                         remove { Events.RemoveHandler (TreeNodeDataBoundEvent, value); }
217                 }
218                 
219                 public event TreeNodeEventHandler TreeNodeExpanded {
220                         add { Events.AddHandler (TreeNodeExpandedEvent, value); }
221                         remove { Events.RemoveHandler (TreeNodeExpandedEvent, value); }
222                 }
223                 
224                 public event TreeNodeEventHandler TreeNodePopulate {
225                         add { Events.AddHandler (TreeNodePopulateEvent, value); }
226                         remove { Events.RemoveHandler (TreeNodePopulateEvent, value); }
227                 }
228                 
229                 protected virtual void OnTreeNodeCheckChanged (TreeNodeEventArgs e)
230                 {
231                         if (Events != null) {
232                                 TreeNodeEventHandler eh = (TreeNodeEventHandler) Events [TreeNodeCheckChangedEvent];
233                                 if (eh != null)
234                                         eh (this, e);
235                         }
236                 }
237
238                 protected virtual void OnSelectedNodeChanged (EventArgs e)
239                 {
240                         if (Events != null) {
241                                 EventHandler eh = (EventHandler) Events [SelectedNodeChangedEvent];
242                                 if (eh != null)
243                                         eh (this, e);
244                         }
245                 }
246
247                 protected virtual void OnTreeNodeCollapsed (TreeNodeEventArgs e)
248                 {
249                         if (Events != null) {
250                                 TreeNodeEventHandler eh = (TreeNodeEventHandler) Events [TreeNodeCollapsedEvent];
251                                 if (eh != null)
252                                         eh (this, e);
253                         }
254                 }
255
256                 protected virtual void OnTreeNodeDataBound (TreeNodeEventArgs e)
257                 {
258                         if (Events != null) {
259                                 TreeNodeEventHandler eh = (TreeNodeEventHandler) Events [TreeNodeDataBoundEvent];
260                                 if (eh != null)
261                                         eh (this, e);
262                         }
263                 }
264
265                 protected virtual void OnTreeNodeExpanded (TreeNodeEventArgs e)
266                 {
267                         if (Events != null) {
268                                 TreeNodeEventHandler eh = (TreeNodeEventHandler) Events [TreeNodeExpandedEvent];
269                                 if (eh != null)
270                                         eh (this, e);
271                         }
272                 }
273
274                 protected virtual void OnTreeNodePopulate (TreeNodeEventArgs e)
275                 {
276                         if (Events != null) {
277                                 TreeNodeEventHandler eh = (TreeNodeEventHandler) Events [TreeNodePopulateEvent];
278                                 if (eh != null)
279                                         eh (this, e);
280                         }
281                 }
282
283
284                 [Localizable (true)]
285                 public string CollapseImageToolTip {
286                         get { return ViewState.GetString ("CollapseImageToolTip", "Collapse {0}"); }
287                         set { ViewState["CollapseImageToolTip"] = value; }
288                 }
289
290                 [MonoTODO ("Implement support for this")]
291                 [WebCategory ("Behavior")]
292                 [WebSysDescription ("Whether the tree will automatically generate bindings.")]
293                 [DefaultValue (true)]
294                 public bool AutoGenerateDataBindings {
295                         get { return ViewState.GetBool ("AutoGenerateDataBindings", true); }
296                         set { ViewState["AutoGenerateDataBindings"] = value; }
297                 }
298
299                 [DefaultValue ("")]
300                 [WebSysDescription ("The url of the image to show when a node can be collapsed.")]
301                 [UrlProperty]
302                 [WebCategory ("Appearance")]
303                 [Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
304                 public string CollapseImageUrl {
305                         get { return ViewState.GetString ("CollapseImageUrl", String.Empty); }
306                         set { ViewState["CollapseImageUrl"] = value; }
307                 }
308
309                 [WebCategory ("Data")]
310                 [PersistenceMode (PersistenceMode.InnerProperty)]
311                 [WebSysDescription ("Bindings for tree nodes.")]
312                 [Editor ("System.Web.UI.Design.WebControls.TreeViewBindingsEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
313                 [DefaultValue (null)]
314                 [MergablePropertyAttribute (false)]
315                 public TreeNodeBindingCollection DataBindings {
316                         get {
317                                 if (dataBindings == null) {
318                                         dataBindings = new TreeNodeBindingCollection ();
319                                         if (IsTrackingViewState)
320                                                 ((IStateManager)dataBindings).TrackViewState();
321                                 }
322                                 return dataBindings;
323                         }
324                 }
325
326                 [WebCategory ("Behavior")]
327                 [WebSysDescription ("Whether the tree view can use client-side script to expand and collapse nodes.")]
328                 [Themeable (false)]
329                 [DefaultValue (true)]
330                 public bool EnableClientScript {
331                         get { return ViewState.GetBool ("EnableClientScript", true); }
332                         set { ViewState["EnableClientScript"] = value; }
333                 }
334
335                 [DefaultValue (-1)]
336                 [WebCategory ("Behavior")]
337                 [WebSysDescription ("The initial expand depth.")]
338                 [TypeConverter ("System.Web.UI.WebControls.TreeView+TreeViewExpandDepthConverter, " + Consts.AssemblySystem_Web)]
339                 public int ExpandDepth {
340                         get { return ViewState.GetInt ("ExpandDepth", -1); }
341                         set { ViewState["ExpandDepth"] = value; }
342                 }
343
344                 [Localizable (true)]
345                 public string ExpandImageToolTip {
346                         get { return ViewState.GetString ("ExpandImageToolTip", "Expand {0}"); }
347                         set { ViewState["ExpandImageToolTip"] = value; }
348                 }
349
350                 [DefaultValue ("")]
351                 [UrlProperty]
352                 [WebSysDescription ("The url of the image to show when a node can be expanded.")]
353                 [WebCategory ("Appearance")]
354                 [Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
355                 public string ExpandImageUrl {
356                         get { return ViewState.GetString ("ExpandImageUrl", String.Empty); }
357                         set { ViewState["ExpandImageUrl"] = value; }
358                 }
359
360                 [PersistenceMode (PersistenceMode.InnerProperty)]
361                 [NotifyParentProperty (true)]
362                 [DefaultValue (null)]
363                 [WebCategory ("Styles")]
364                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
365                 public Style HoverNodeStyle {
366                         get {
367                                 if (hoverNodeStyle == null) {
368                                         hoverNodeStyle = new Style();
369                                         if (IsTrackingViewState)
370                                                 hoverNodeStyle.TrackViewState();
371                                 }
372                                 return hoverNodeStyle;
373                         }
374                 }
375
376                 [DefaultValue (TreeViewImageSet.Custom)]
377                 public TreeViewImageSet ImageSet {
378                         get { return (TreeViewImageSet)ViewState.GetInt ("ImageSet", (int)TreeViewImageSet.Custom); }
379                         set {
380                                 if (!Enum.IsDefined (typeof (TreeViewImageSet), value))
381                                         throw new ArgumentOutOfRangeException ();
382                                 ViewState["ImageSet"] = value;
383                         }
384                 }
385
386                 [PersistenceMode (PersistenceMode.InnerProperty)]
387                 [NotifyParentProperty (true)]
388                 [DefaultValue (null)]
389                 [WebCategory ("Styles")]
390                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
391                 public TreeNodeStyle LeafNodeStyle {
392                         get {
393                                 if (leafNodeStyle == null) {
394                                         leafNodeStyle = new TreeNodeStyle ();
395                                         if (IsTrackingViewState)
396                                                 leafNodeStyle.TrackViewState();
397                                 }
398                                 return leafNodeStyle;
399                         }
400                 }
401                 
402                 [DefaultValue (null)]
403                 [WebCategory ("Styles")]
404                 [PersistenceMode (PersistenceMode.InnerProperty)]
405                 [Editor ("System.Web.UI.Design.WebControls.TreeNodeStyleCollectionEditor," + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
406                 public TreeNodeStyleCollection LevelStyles {
407                         get {
408                                 if (levelStyles == null) {
409                                         levelStyles = new TreeNodeStyleCollection ();
410                                         if (IsTrackingViewState)
411                                                 ((IStateManager)levelStyles).TrackViewState();
412                                 }
413                                 return levelStyles;
414                         }
415                 }
416
417                 [DefaultValue ("")]
418                 public string LineImagesFolder {
419                         get { return ViewState.GetString ("LineImagesFolder", String.Empty); }
420                         set { ViewState["LineImagesFolder"] = value; }
421                 }
422
423                 [DefaultValue (-1)]
424                 public int MaxDataBindDepth {
425                         get { return ViewState.GetInt ("MaxDataBindDepth", -1); }
426                         set { ViewState["MaxDataBindDepth"] = value; }
427                 }
428
429                 [DefaultValue (20)]
430                 public int NodeIndent {
431                         get { return ViewState.GetInt ("NodeIndent", 20); }
432                         set { ViewState["NodeIndent"] = value; }
433                 }
434                 
435                 [PersistenceMode (PersistenceMode.InnerProperty)]
436                 [Editor ("System.Web.UI.Design.WebControls.TreeNodeCollectionEditor," + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
437                 [DefaultValueAttribute (null)]
438                 [MergablePropertyAttribute (false)]
439                 public TreeNodeCollection Nodes {
440                         get {
441                                 if (nodes == null) {
442                                         nodes = new TreeNodeCollection (this);
443                                         if (IsTrackingViewState)
444                                                 ((IStateManager)nodes).TrackViewState();
445                                 }
446                                 return nodes;
447                         }
448                 }
449
450                 [PersistenceMode (PersistenceMode.InnerProperty)]
451                 [NotifyParentProperty (true)]
452                 [DefaultValue (null)]
453                 [WebCategory ("Styles")]
454                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
455                 public TreeNodeStyle NodeStyle {
456                         get {
457                                 if (nodeStyle == null) {
458                                         nodeStyle = new TreeNodeStyle ();
459                                         if (IsTrackingViewState)
460                                                 nodeStyle.TrackViewState();
461                                 }
462                                 return nodeStyle;
463                         }
464                 }
465                 
466                 [DefaultValue (false)]
467                 public bool NodeWrap {
468                         get { return ViewState.GetBool ("NodeWrap", false); }
469                         set { ViewState ["NodeWrap"] = value; }
470                 }
471
472                 [UrlProperty]
473                 [DefaultValue ("")]
474                 [WebSysDescription ("The url of the image to show for leaf nodes.")]
475                 [WebCategory ("Appearance")]
476                 [Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
477                 public string NoExpandImageUrl {
478                         get { return ViewState.GetString ("NoExpandImageUrl", String.Empty); }
479                         set { ViewState ["NoExpandImageUrl"] = value; }
480                 }
481
482                 [PersistenceMode (PersistenceMode.InnerProperty)]
483                 [NotifyParentProperty (true)]
484                 [DefaultValue (null)]
485                 [WebCategory ("Styles")]
486                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
487                 public TreeNodeStyle ParentNodeStyle {
488                         get {
489                                 if (parentNodeStyle == null) {
490                                         parentNodeStyle = new TreeNodeStyle ();
491                                         if (IsTrackingViewState)
492                                                 parentNodeStyle.TrackViewState();
493                                 }
494                                 return parentNodeStyle;
495                         }
496                 }
497                 
498                 [DefaultValue ('/')]
499                 public char PathSeparator {
500                         get { return ViewState.GetChar ("PathSeparator", '/'); }
501                         set { ViewState ["PathSeparator"] = value; }
502                 }
503
504                 [DefaultValue (true)]
505                 public bool PopulateNodesFromClient {
506                         get { return ViewState.GetBool ("PopulateNodesFromClient", true); }
507                         set { ViewState ["PopulateNodesFromClient"] = value; }
508                 }
509
510                 [PersistenceMode (PersistenceMode.InnerProperty)]
511                 [NotifyParentProperty (true)]
512                 [DefaultValue (null)]
513                 [WebCategory ("Styles")]
514                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
515                 public TreeNodeStyle RootNodeStyle {
516                         get {
517                                 if (rootNodeStyle == null) {
518                                         rootNodeStyle = new TreeNodeStyle ();
519                                         if (IsTrackingViewState)
520                                                 rootNodeStyle.TrackViewState();
521                                 }
522                                 return rootNodeStyle;
523                         }
524                 }
525                 
526                 [PersistenceMode (PersistenceMode.InnerProperty)]
527                 [NotifyParentProperty (true)]
528                 [DefaultValue (null)]
529                 [WebCategory ("Styles")]
530                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
531                 public TreeNodeStyle SelectedNodeStyle {
532                         get {
533                                 if (selectedNodeStyle == null) {
534                                         selectedNodeStyle = new TreeNodeStyle ();
535                                         if (IsTrackingViewState)
536                                                 selectedNodeStyle.TrackViewState();
537                                 }
538                                 return selectedNodeStyle;
539                         }
540                 }
541
542                 Style ControlLinkStyle {
543                         get {
544                                 if (controlLinkStyle == null) {
545                                         controlLinkStyle = new Style ();
546                                         controlLinkStyle.AlwaysRenderTextDecoration = true;
547                                 }
548                                 return controlLinkStyle;
549                         }
550                 }
551
552                 Style NodeLinkStyle {
553                         get {
554                                 if (nodeLinkStyle == null) {
555                                         nodeLinkStyle = new Style ();
556                                 }
557                                 return nodeLinkStyle;
558                         }
559                 }
560
561                 Style RootNodeLinkStyle {
562                         get {
563                                 if (rootNodeLinkStyle == null)
564                                         rootNodeLinkStyle = new Style ();
565                                 return rootNodeLinkStyle;
566                         }
567                 }
568
569                 Style ParentNodeLinkStyle {
570                         get {
571                                 if (parentNodeLinkStyle == null)
572                                         parentNodeLinkStyle = new Style ();
573                                 return parentNodeLinkStyle;
574                         }
575                 }
576
577                 Style SelectedNodeLinkStyle {
578                         get {
579                                 if (selectedNodeLinkStyle == null)
580                                         selectedNodeLinkStyle = new Style ();
581                                 return selectedNodeLinkStyle;
582                         }
583                 }
584
585                 Style LeafNodeLinkStyle {
586                         get {
587                                 if (leafNodeLinkStyle == null)
588                                         leafNodeLinkStyle = new Style ();
589                                 return leafNodeLinkStyle;
590                         }
591                 }
592
593                 Style HoverNodeLinkStyle {
594                         get {
595                                 if (hoverNodeLinkStyle == null)
596                                         hoverNodeLinkStyle = new Style ();
597                                 return hoverNodeLinkStyle;
598                         }
599                 }
600                 
601                 [DefaultValue (TreeNodeTypes.None)]
602                 public TreeNodeTypes ShowCheckBoxes {
603                         get { return (TreeNodeTypes)ViewState.GetInt ("ShowCheckBoxes", (int)TreeNodeTypes.None); }
604                         set {
605                                 if ((int) value > 7)
606                                         throw new ArgumentOutOfRangeException ();
607                                 ViewState ["ShowCheckBoxes"] = value;
608                         }
609                 }
610
611                 [DefaultValue (true)]
612                 public bool ShowExpandCollapse {
613                         get { return ViewState.GetBool ("ShowExpandCollapse", true); }
614                         set { ViewState ["ShowExpandCollapse"] = value; }
615                 }
616
617                 [DefaultValue (false)]
618                 public bool ShowLines {
619                         get { return ViewState.GetBool ("ShowLines", false); }
620                         set { ViewState ["ShowLines"] = value; }
621                 }
622
623                 [Localizable (true)]
624                 public string SkipLinkText
625                 {
626                         get { return ViewState.GetString ("SkipLinkText", "Skip Navigation Links."); }
627                         set { ViewState ["SkipLinkText"] = value; }
628                 }
629                 
630                 
631                 [Browsable (false)]
632                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
633                 public TreeNode SelectedNode {
634                         get { return selectedNode; }
635                 }
636
637                 [Browsable (false)]
638                 [DefaultValue ("")]
639                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
640                 public string SelectedValue {
641                         get { return selectedNode != null ? selectedNode.Value : String.Empty; }
642                 }
643
644                 [DefaultValue ("")]
645                 public string Target {
646                         get { return ViewState.GetString ("Target", String.Empty); }
647                         set { ViewState ["Target"] = value; }
648                 }
649
650                 [MonoTODO ("why override?")]
651                 public override bool Visible 
652                 {
653                         get { return base.Visible; }
654                         set { base.Visible = value; }
655                 }
656                                 
657                 [Browsable (false)]
658                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
659                 public TreeNodeCollection CheckedNodes {
660                         get {
661                                 TreeNodeCollection col = new TreeNodeCollection ();
662                                 FindCheckedNodes (Nodes, col);
663                                 return col;
664                         }
665                 }
666                 
667                 void FindCheckedNodes (TreeNodeCollection nodeList, TreeNodeCollection result)
668                 {
669                         foreach (TreeNode node in nodeList) {
670                                 if (node.Checked)
671                                         result.Add (node, false);
672                                 FindCheckedNodes (node.ChildNodes, result);
673                         }
674                 }
675                 
676                 public void ExpandAll ()
677                 {
678                         foreach (TreeNode node in Nodes)
679                                 node.ExpandAll ();
680                 }
681                 
682                 public void CollapseAll ()
683                 {
684                         foreach (TreeNode node in Nodes)
685                                 node.CollapseAll ();
686                 }
687                 
688                 public TreeNode FindNode (string valuePath)
689                 {
690                         if (valuePath == null)
691                                 throw new ArgumentNullException ("valuePath");
692                         string[] path = valuePath.Split (PathSeparator);
693                         int n = 0;
694                         TreeNodeCollection col = Nodes;
695                         bool foundBranch = true;
696                         while (col.Count > 0 && foundBranch) {
697                                 foundBranch = false;
698                                 foreach (TreeNode node in col) {
699                                         if (node.Value == path [n]) {
700                                                 if (++n == path.Length)
701                                                         return node;
702                                                 col = node.ChildNodes;
703                                                 foundBranch = true;
704                                                 break;
705                                         }
706                                 }
707                         }
708                         return null;
709                 }
710                 
711                 ImageStyle GetImageStyle ()
712                 {
713                         if (ImageSet != TreeViewImageSet.Custom)
714                                 return (ImageStyle) imageStyles [ImageSet];
715                         else
716                                 return null;
717                 }
718                 
719                 protected override HtmlTextWriterTag TagKey {
720                         get { return HtmlTextWriterTag.Div; }
721                 }
722                 
723                 protected internal virtual TreeNode CreateNode ()
724                 {
725                         return new TreeNode (this);
726                 }
727                 
728                 public sealed override void DataBind ()
729                 {
730                         base.DataBind ();
731                 }
732                 
733                 protected void SetNodeDataBound (TreeNode node, bool dataBound)
734                 {
735                         node.SetDataBound (dataBound);
736                 }
737                 
738                 protected void SetNodeDataPath (TreeNode node, string dataPath)
739                 {
740                         node.SetDataPath (dataPath);
741                 }
742                 
743                 protected void SetNodeDataItem (TreeNode node, object dataItem)
744                 {
745                         node.SetDataItem (dataItem);
746                 }
747                 
748                 protected internal override void OnInit (EventArgs e)
749                 {
750                         base.OnInit (e);
751                 }
752                 
753                 internal void SetSelectedNode (TreeNode node, bool loading)
754                 {
755                         if (selectedNode == node)
756                                 return;
757                         if (selectedNode != null)
758                                 selectedNode.SelectedFlag = false;
759                         selectedNode = node;
760                         if (!loading)
761                                 OnSelectedNodeChanged (new TreeNodeEventArgs (selectedNode));
762                 }
763                 
764                 internal void NotifyCheckChanged (TreeNode node)
765                 {
766                         OnTreeNodeCheckChanged (new TreeNodeEventArgs (node));
767                 }
768
769                 internal void NotifyExpandedChanged (TreeNode node)
770                 {
771                         if (node.Expanded.HasValue && node.Expanded.Value)
772                                 OnTreeNodeExpanded (new TreeNodeEventArgs (node));
773                         else if (node.Expanded.HasValue && node.IsParentNode)
774                                 OnTreeNodeCollapsed (new TreeNodeEventArgs (node));
775                 }
776
777                 internal void NotifyPopulateRequired (TreeNode node)
778                 {
779                         OnTreeNodePopulate (new TreeNodeEventArgs (node));
780                 }
781
782                 protected override void TrackViewState()
783                 {
784                         EnsureDataBound ();
785                         
786                         base.TrackViewState();
787                         if (hoverNodeStyle != null)
788                                 hoverNodeStyle.TrackViewState();
789                         if (leafNodeStyle != null)
790                                 leafNodeStyle.TrackViewState();
791                         if (levelStyles != null && levelStyles.Count > 0)
792                                 ((IStateManager)levelStyles).TrackViewState();
793                         if (nodeStyle != null)
794                                 nodeStyle.TrackViewState();
795                         if (parentNodeStyle != null)
796                                 parentNodeStyle.TrackViewState();
797                         if (rootNodeStyle != null)
798                                 rootNodeStyle.TrackViewState();
799                         if (selectedNodeStyle != null)
800                                 selectedNodeStyle.TrackViewState();
801                         if (dataBindings != null)
802                                 ((IStateManager)dataBindings).TrackViewState ();
803                         if (nodes != null)
804                                 ((IStateManager)nodes).TrackViewState();;
805                 }
806
807                 protected override object SaveViewState()
808                 {
809                         object[] states = new object [10];
810                         states[0] = base.SaveViewState();
811                         states[1] = (hoverNodeStyle == null ? null : hoverNodeStyle.SaveViewState());
812                         states[2] = (leafNodeStyle == null ? null : leafNodeStyle.SaveViewState());
813                         states[3] = (levelStyles == null ? null : ((IStateManager)levelStyles).SaveViewState());
814                         states[4] = (nodeStyle == null ? null : nodeStyle.SaveViewState());
815                         states[5] = (parentNodeStyle == null ? null : parentNodeStyle.SaveViewState());
816                         states[6] = (rootNodeStyle == null ? null : rootNodeStyle.SaveViewState());
817                         states[7] = (selectedNodeStyle == null ? null : selectedNodeStyle.SaveViewState());
818                         states[8] = (dataBindings == null ? null : ((IStateManager)dataBindings).SaveViewState());
819                         states[9] = (nodes == null ? null : ((IStateManager)nodes).SaveViewState());
820
821                         for (int i = states.Length - 1; i >= 0; i--) {
822                                 if (states [i] != null)
823                                         return states;
824                         }
825                         
826                         return null;
827                 }
828
829                 protected override void LoadViewState (object savedState)
830                 {
831                         if (savedState == null)
832                                 return;
833                                 
834                         object [] states = (object []) savedState;
835                         base.LoadViewState (states[0]);
836                         
837                         if (states[1] != null)
838                                 HoverNodeStyle.LoadViewState (states[1]);
839                         if (states[2] != null)
840                                 LeafNodeStyle.LoadViewState(states[2]);
841                         if (states[3] != null)
842                                 ((IStateManager)LevelStyles).LoadViewState(states[3]);
843                         if (states[4] != null)
844                                 NodeStyle.LoadViewState(states[4]);
845                         if (states[5] != null)
846                                 ParentNodeStyle.LoadViewState(states[5]);
847                         if (states[6] != null)
848                                 RootNodeStyle.LoadViewState(states[6]);
849                         if (states[7] != null)
850                                 SelectedNodeStyle.LoadViewState(states[7]);
851                         if (states[8] != null)
852                                 ((IStateManager)DataBindings).LoadViewState(states[8]);
853                         if (states[9] != null)
854                                 ((IStateManager)Nodes).LoadViewState(states[9]);
855                 }
856
857                 protected virtual void RaisePostBackEvent (string eventArgument)
858                 {
859                         ValidateEvent (UniqueID, eventArgument);
860                         string[] args = eventArgument.Split ('|');
861                         TreeNode node = FindNodeByPos (args[1]);
862                         if (node == null)
863                                 return;
864                         
865                         if (args [0] == "sel")
866                                 HandleSelectEvent (node);
867                         else if (args [0] == "ec")
868                                 HandleExpandCollapseEvent (node);
869                 }
870                 
871                 void HandleSelectEvent (TreeNode node)
872                 {
873                         switch (node.SelectAction) {
874                                 case TreeNodeSelectAction.Select:
875                                         node.Select ();
876                                         break;
877                                 case TreeNodeSelectAction.Expand:
878                                         node.Expand ();
879                                         break;
880                                 case TreeNodeSelectAction.SelectExpand:
881                                         node.Select ();
882                                         node.Expand ();
883                                         break;
884                         }
885                 }
886                 
887                 void HandleExpandCollapseEvent (TreeNode node)
888                 {
889                         node.ToggleExpandState ();
890                 }
891                 
892                 protected virtual void RaisePostDataChangedEvent ()
893                 {
894                 }
895
896                 TreeNode MakeNodeTree (string[] args)
897                 {
898                         string[] segments = args [0].Split ('_');
899                         TreeNode ret = null, node;
900
901                         foreach (string seg in segments) {
902                                 int idx = Int32.Parse (seg);
903                                 node = new TreeNode (seg);
904                                 if (ret != null) {
905                                         ret.ChildNodes.Add (node);
906                                         node.Index = idx;
907                                 }
908                                 ret = node;
909                         }
910
911                         ret.Value = args [1].Replace ("U+007C", "|");
912                         ret.ImageUrl = args [2].Replace ("U+007C", "|");
913                         ret.NavigateUrl = args [3].Replace ("U+007C", "|");
914                         ret.Target = args [4].Replace ("U+007C", "|");
915                         ret.Tree = this;
916                         
917                         NotifyPopulateRequired (ret);
918                         
919                         return ret;
920                 }
921                 
922                 string callbackResult;
923                 protected virtual void RaiseCallbackEvent (string eventArgs)
924                 {
925                         string[] args = eventArgs.Split ('|');
926                         RequiresDataBinding = true;
927                         EnsureDataBound ();
928                         
929                         TreeNode node = MakeNodeTree (args);
930                         ArrayList levelLines = new ArrayList ();
931                         TreeNode nd = node;
932                         while (nd != null) {
933                                 int childCount = nd.Parent != null ? nd.Parent.ChildNodes.Count : Nodes.Count;
934                                 levelLines.Insert (0, (nd.Index < childCount - 1) ? this : null);
935                                 nd = nd.Parent;
936                         }
937                         
938                         StringWriter sw = new StringWriter ();
939                         HtmlTextWriter writer = new HtmlTextWriter (sw);
940                         EnsureStylesPrepared ();
941
942                         node.Expanded = true;
943                         int num = node.ChildNodes.Count;
944                         for (int n=0; n<num; n++)
945                                 RenderNode (writer, node.ChildNodes [n], node.Depth + 1, levelLines, true, n<num-1);
946                         
947                         string res = sw.ToString ();
948                         callbackResult = res.Length > 0 ? res : "*";
949                 }
950                 
951                 protected virtual string GetCallbackResult ()
952                 {
953                         return callbackResult;
954                 }
955
956                 void IPostBackEventHandler.RaisePostBackEvent (string eventArgument)
957                 {
958                         RaisePostBackEvent (eventArgument);
959                 }
960                 
961                 bool IPostBackDataHandler.LoadPostData (string postDataKey, NameValueCollection postCollection)
962                 {
963                         return LoadPostData (postDataKey, postCollection);
964                 }
965                 
966                 void IPostBackDataHandler.RaisePostDataChangedEvent ()
967                 {
968                         RaisePostDataChangedEvent ();
969                 }
970                 
971                 void ICallbackEventHandler.RaiseCallbackEvent (string eventArgs)
972                 {
973                         RaiseCallbackEvent (eventArgs);
974                 }
975                 
976                 string ICallbackEventHandler.GetCallbackResult ()
977                 {
978                         return GetCallbackResult ();
979                 }
980
981                 protected override ControlCollection CreateControlCollection ()
982                 {
983                         return new EmptyControlCollection (this);
984                 }
985                 
986                 protected internal override void PerformDataBinding ()
987                 {
988                         base.PerformDataBinding ();
989                         InitializeDataBindings ();
990                         HierarchicalDataSourceView data = GetData (String.Empty);
991                         if (data == null)
992                                 return;
993                         Nodes.Clear ();
994                         IHierarchicalEnumerable e = data.Select ();
995                         FillBoundChildrenRecursive (e, Nodes);
996                 }
997                 
998                 void FillBoundChildrenRecursive (IHierarchicalEnumerable hEnumerable, TreeNodeCollection nodeCollection)
999                 {
1000                         if (hEnumerable == null)
1001                                 return;                 
1002                         
1003                         foreach (object obj in hEnumerable) {
1004                                 IHierarchyData hdata = hEnumerable.GetHierarchyData (obj);
1005                                 TreeNode child = new TreeNode ();
1006                                 nodeCollection.Add (child);
1007                                 child.Bind (hdata);
1008                                 OnTreeNodeDataBound (new TreeNodeEventArgs (child));
1009
1010                                 if (MaxDataBindDepth >= 0 && child.Depth == MaxDataBindDepth)
1011                                         continue;
1012
1013                                 if (hdata == null || !hdata.HasChildren)
1014                                         continue;
1015
1016                                 IHierarchicalEnumerable e = hdata.GetChildren ();
1017                                 FillBoundChildrenRecursive (e, child.ChildNodes);
1018                         }
1019                 }
1020                 
1021                 protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection)
1022                 {
1023                         bool res = false;
1024
1025                         if (EnableClientScript && PopulateNodesFromClient) {
1026                                 string states = postCollection [ClientID + "_PopulatedStates"];
1027                                 if (states != null) {
1028                                         foreach (string id in states.Split ('|')) {
1029                                                 if (String.IsNullOrEmpty(id))
1030                                                         continue;
1031                                                 TreeNode node = FindNodeByPos (id);
1032                                                 if (node != null && node.PopulateOnDemand && !node.Populated)
1033                                                         node.Populated = true;
1034                                         }
1035                                 }
1036                                 res = true;
1037                         }
1038
1039                         UnsetCheckStates (Nodes, postCollection);
1040                         SetCheckStates (postCollection);
1041                         
1042                         if (EnableClientScript) {
1043                                 string states = postCollection [ClientID + "_ExpandStates"];
1044                                 if (states != null) {
1045                                         string[] ids = states.Split ('|');
1046                                         UnsetExpandStates (Nodes, ids);
1047                                         SetExpandStates (ids);
1048                                 } else
1049                                         UnsetExpandStates (Nodes, new string[0]);
1050                                 res = true;
1051                         }
1052                         return res;
1053                 }
1054
1055                 const string _OnPreRender_Script_Preamble =
1056                         "var {0} = new Object ();\n" +
1057                         "{0}.treeId = {1};\n" +
1058                         "{0}.uid = {2};\n" +
1059                         "{0}.showImage = {3};\n";
1060
1061                 const string _OnPreRender_Script_ShowExpandCollapse =
1062                         "{0}.expandImage = {1};\n" +
1063                         "{0}.collapseImage = {2};\n";
1064
1065                 const string _OnPreRender_Script_ShowExpandCollapse_Populate =
1066                         "{0}.noExpandImage = {1};\n";
1067
1068                 const string _OnPreRender_Script_PopulateCallback =
1069                         "{0}.form = {1};\n" +
1070                         "{0}.PopulateNode = function (nodeId, nodeValue, nodeImageUrl, nodeNavigateUrl, nodeTarget) {{\n" +
1071                         "\t{2}.__theFormPostData = \"\";\n" +
1072                         "\t{2}.__theFormPostCollection = new Array ();\n" +
1073                         "\t{2}.WebForm_InitCallback ();\n" +
1074                         "\tTreeView_PopulateNode (this.uid, this.treeId, nodeId, nodeValue, nodeImageUrl, nodeNavigateUrl, nodeTarget)\n}};\n";
1075
1076                 const string _OnPreRender_Script_CallbackOptions =
1077                         "{0}.populateFromClient = {1};\n" +
1078                         "{0}.expandAlt = {2};\n" +
1079                         "{0}.collapseAlt = {3};\n";
1080
1081                 const string _OnPreRender_Script_HoverStyle =
1082                         "{0}.hoverClass = {1};\n" +
1083                         "{0}.hoverLinkClass = {2};\n";
1084                 
1085                 protected internal override void OnPreRender (EventArgs e)
1086                 {
1087                         base.OnPreRender (e);
1088
1089                         if (Page != null) {
1090                                 if (Enabled)
1091                                         Page.RegisterRequiresPostBack (this);
1092                         
1093                                 if (EnableClientScript && !Page.ClientScript.IsClientScriptIncludeRegistered (typeof(TreeView), "TreeView.js")) {
1094                                         string url = Page.ClientScript.GetWebResourceUrl (typeof(TreeView), "TreeView.js");
1095                                         Page.ClientScript.RegisterClientScriptInclude (typeof(TreeView), "TreeView.js", url);
1096                                 }
1097                         }
1098                         
1099                         string ctree = ClientID + "_data";
1100                         StringBuilder script = new StringBuilder ();
1101                         script.AppendFormat (_OnPreRender_Script_Preamble,
1102                                              ctree,
1103                                              ClientScriptManager.GetScriptLiteral (ClientID),
1104                                              ClientScriptManager.GetScriptLiteral (UniqueID),
1105                                              ClientScriptManager.GetScriptLiteral (ShowExpandCollapse));                        
1106                         
1107                         if (ShowExpandCollapse) {
1108                                 ImageStyle imageStyle = GetImageStyle ();
1109                                 script.AppendFormat (_OnPreRender_Script_ShowExpandCollapse,
1110                                                      ctree,
1111                                                      ClientScriptManager.GetScriptLiteral (GetNodeImageUrl ("plus", imageStyle)),
1112                                                      ClientScriptManager.GetScriptLiteral (GetNodeImageUrl ("minus", imageStyle)));
1113                                                      
1114                                 if (PopulateNodesFromClient)
1115                                         script.AppendFormat (_OnPreRender_Script_ShowExpandCollapse_Populate,
1116                                                              ctree,
1117                                                              ClientScriptManager.GetScriptLiteral (GetNodeImageUrl ("noexpand", imageStyle)));
1118                         }
1119
1120                         if (Page != null) {
1121                                 script.AppendFormat (_OnPreRender_Script_PopulateCallback,
1122                                                      ctree,
1123                                                      Page.theForm,
1124                                                      Page.WebFormScriptReference);
1125                                 
1126                                                      // Page.ClientScript.GetCallbackEventReference (
1127                                                      //              "this.uid", "nodeId",
1128                                                      //              "TreeView_PopulateCallback",
1129                                                      //              "this.treeId + \" \" + nodeId", "TreeView_PopulateCallback", false));
1130                                 
1131                                 script.AppendFormat (_OnPreRender_Script_CallbackOptions,
1132                                                      ctree,
1133                                                      ClientScriptManager.GetScriptLiteral (PopulateNodesFromClient),
1134                                                      ClientScriptManager.GetScriptLiteral (GetNodeImageToolTip (true, null)),
1135                                                      ClientScriptManager.GetScriptLiteral (GetNodeImageToolTip (false, null)));
1136                                 
1137                                 if (!Page.IsPostBack)
1138                                         SetNodesExpandedToDepthRecursive (Nodes);
1139
1140                                 if (EnableClientScript) {
1141                                         Page.ClientScript.RegisterHiddenField (ClientID + "_ExpandStates", GetExpandStates ());
1142
1143                                         // Make sure the basic script infrastructure is rendered
1144                                         Page.ClientScript.RegisterWebFormClientScript ();
1145                                 }
1146
1147                                 if (EnableClientScript && PopulateNodesFromClient)
1148                                         Page.ClientScript.RegisterHiddenField (ClientID + "_PopulatedStates", "|");
1149
1150                                 EnsureStylesPrepared ();
1151
1152                                 if (hoverNodeStyle != null) {
1153                                         if (Page.Header == null)
1154                                                 throw new InvalidOperationException ("Using TreeView.HoverNodeStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
1155                                         RegisterStyle (HoverNodeStyle, HoverNodeLinkStyle);
1156                                         script.AppendFormat (_OnPreRender_Script_HoverStyle,
1157                                                              ctree,
1158                                                              ClientScriptManager.GetScriptLiteral (HoverNodeStyle.RegisteredCssClass),
1159                                                              ClientScriptManager.GetScriptLiteral (HoverNodeLinkStyle.RegisteredCssClass));                                     
1160                                 }
1161                                 
1162                                 Page.ClientScript.RegisterStartupScript (typeof(TreeView), this.UniqueID, script.ToString (), true);
1163                                 script = null;
1164                         }
1165                 }
1166
1167                 void EnsureStylesPrepared ()
1168                 {
1169                         if (stylesPrepared)
1170                                 return;
1171                         stylesPrepared = true;
1172                         PrepareStyles ();
1173                 }
1174
1175                 void PrepareStyles ()
1176                 {
1177                         // The order in which styles are defined matters when more than one class
1178                         // is assigned to an element
1179                         ControlLinkStyle.CopyTextStylesFrom (ControlStyle);
1180                         RegisterStyle (ControlLinkStyle);
1181
1182                         if (nodeStyle != null)
1183                                 RegisterStyle (NodeStyle, NodeLinkStyle);
1184
1185                         if (rootNodeStyle != null)
1186                                 RegisterStyle (RootNodeStyle, RootNodeLinkStyle);
1187
1188                         if (parentNodeStyle != null)
1189                                 RegisterStyle (ParentNodeStyle, ParentNodeLinkStyle);
1190
1191                         if (leafNodeStyle != null)
1192                                 RegisterStyle (LeafNodeStyle, LeafNodeLinkStyle);
1193
1194
1195                         if (levelStyles != null && levelStyles.Count > 0) {
1196                                 levelLinkStyles = new List<Style> (levelStyles.Count);
1197                                 foreach (Style style in levelStyles) {
1198                                         Style linkStyle = new Style ();
1199                                         levelLinkStyles.Add (linkStyle);
1200                                         RegisterStyle (style, linkStyle);
1201                                 }
1202                         }
1203
1204                         if (selectedNodeStyle != null)
1205                                 RegisterStyle (SelectedNodeStyle, SelectedNodeLinkStyle);
1206                 }
1207
1208                 void SetNodesExpandedToDepthRecursive (TreeNodeCollection nodes)
1209                 {
1210                         foreach (TreeNode node in nodes) {
1211                                 if (!node.Expanded.HasValue) {
1212                                         if (ExpandDepth < 0 || node.Depth < ExpandDepth)
1213                                                 node.Expanded = true;
1214                                 }
1215                                 SetNodesExpandedToDepthRecursive (node.ChildNodes);
1216                         }
1217                 }
1218
1219                 string IncrementStyleClassName ()
1220                 {
1221                         registeredStylesCounter++;
1222                         return ClientID + "_" + registeredStylesCounter;
1223                 }
1224
1225                 void RegisterStyle (Style baseStyle, Style linkStyle)
1226                 {
1227                         linkStyle.CopyTextStylesFrom (baseStyle);
1228                         linkStyle.BorderStyle = BorderStyle.None;
1229                         linkStyle.AddCssClass (baseStyle.CssClass);
1230                         baseStyle.Font.Reset ();
1231                         RegisterStyle (linkStyle);
1232                         RegisterStyle (baseStyle);
1233                 }
1234
1235                 void RegisterStyle (Style baseStyle)
1236                 {
1237                         if (Page.Header == null)
1238                                 return;
1239                         string className = IncrementStyleClassName ().Trim ('_');
1240                         baseStyle.SetRegisteredCssClass (className);
1241                         Page.Header.StyleSheet.CreateStyleRule (baseStyle, this, "." + className);
1242                 }
1243                 
1244                 string GetBindingKey (string dataMember, int depth)
1245                 {
1246                         return dataMember + " " + depth;
1247                 }
1248                 
1249                 void InitializeDataBindings ()
1250                 {
1251                         if (dataBindings != null && dataBindings.Count > 0) {
1252                                 bindings = new Hashtable ();
1253                                 foreach (TreeNodeBinding bin in dataBindings) {
1254                                         string key = GetBindingKey (bin.DataMember, bin.Depth);
1255                                         if (!bindings.ContainsKey(key))
1256                                                 bindings [key] = bin;
1257                                 }
1258                         } else
1259                                 bindings = null;
1260                 }
1261                 
1262                 internal TreeNodeBinding FindBindingForNode (string type, int depth)
1263                 {
1264                         if (bindings == null)
1265                                 return null;
1266                                 
1267                         TreeNodeBinding bin = (TreeNodeBinding) bindings [GetBindingKey (type, depth)];
1268                         if (bin != null)
1269                                 return bin;
1270                         
1271                         bin = (TreeNodeBinding) bindings [GetBindingKey (type, -1)];
1272                         if (bin != null)
1273                                 return bin;
1274                         
1275                         bin = (TreeNodeBinding) bindings [GetBindingKey (String.Empty, depth)];
1276                         if (bin != null)
1277                                 return bin;
1278                         
1279                         return (TreeNodeBinding) bindings [GetBindingKey (String.Empty, -1)];
1280                 }
1281                 
1282                 internal void DecorateNode(TreeNode node)
1283                 {
1284                         if (node == null)
1285                                 return;
1286                         
1287                         if (node.ImageUrl != null && node.ImageUrl.Length > 0)
1288                                 return;
1289
1290                         if (node.IsRootNode && rootNodeStyle != null) {
1291                                 node.ImageUrl = rootNodeStyle.ImageUrl;
1292                                 return;
1293                         }
1294                         if (node.IsParentNode && parentNodeStyle != null) {
1295                                 node.ImageUrl = parentNodeStyle.ImageUrl;
1296                                 return;
1297                         }
1298                         if (node.IsLeafNode && leafNodeStyle != null)
1299                                 node.ImageUrl = leafNodeStyle.ImageUrl;
1300                 }
1301                 
1302                 protected internal override void RenderContents (HtmlTextWriter writer)
1303                 {
1304                         SiteMapDataSource siteMap = GetDataSource () as SiteMapDataSource;
1305                         bool checkSitePath = IsBoundUsingDataSourceID && siteMap != null;
1306
1307                         if (checkSitePath) {
1308                                 IHierarchyData data = siteMap.Provider.CurrentNode;
1309                                 if (data != null)
1310                                         activeSiteMapPath = data.Path;
1311                         }
1312                         
1313                         ArrayList levelLines = new ArrayList ();
1314                         int num = Nodes.Count;
1315                         for (int n=0; n<num; n++)
1316                                 RenderNode (writer, Nodes [n], 0, levelLines, n>0, n<num-1);
1317                 }
1318                 
1319                 protected override void AddAttributesToRender(HtmlTextWriter writer)
1320                 {
1321                         base.AddAttributesToRender (writer);
1322                 }
1323                 
1324                 public override void RenderBeginTag (HtmlTextWriter writer)
1325                 {
1326                         string skipLinkText = SkipLinkText;
1327                         
1328                         if (!String.IsNullOrEmpty (skipLinkText)) {
1329                                 writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + ClientID + "_SkipLink");
1330                                 writer.RenderBeginTag (HtmlTextWriterTag.A);
1331
1332                                 Image img = new Image ();
1333                                 ClientScriptManager csm = new ClientScriptManager (null);
1334                                 img.ImageUrl = csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif");
1335                                 img.Attributes.Add ("height", "0");
1336                                 img.Attributes.Add ("width", "0");
1337                                 img.AlternateText = skipLinkText;
1338                                 img.Render (writer);
1339
1340                                 writer.RenderEndTag ();
1341                         }
1342                         base.RenderBeginTag (writer);
1343                 }
1344                 
1345                 public override void RenderEndTag (HtmlTextWriter writer)
1346                 {
1347                         base.RenderEndTag (writer);
1348
1349                         if (!String.IsNullOrEmpty (SkipLinkText)) {
1350                                 writer.AddAttribute (HtmlTextWriterAttribute.Id, ClientID + "_SkipLink");
1351                                 writer.RenderBeginTag (HtmlTextWriterTag.A);
1352                                 writer.RenderEndTag ();
1353                         }
1354                 }
1355                 
1356                 void RenderNode (HtmlTextWriter writer, TreeNode node, int level, ArrayList levelLines, bool hasPrevious, bool hasNext)
1357                 {
1358                         if (node.PopulateOnDemand && node.HadChildrenBeforePopulating)
1359                                 throw new InvalidOperationException ("PopulateOnDemand cannot be set to true on a node that already has children.");
1360                         
1361                         DecorateNode(node);
1362                         
1363                         string nodeImage;
1364                         bool clientExpand = EnableClientScript && Events [TreeNodeCollapsedEvent] == null && Events [TreeNodeExpandedEvent] == null;
1365                         ImageStyle imageStyle = GetImageStyle ();
1366                         bool renderChildNodes = node.Expanded.HasValue && node.Expanded.Value;
1367                         
1368                         if (clientExpand && !renderChildNodes)
1369                                 renderChildNodes = (!node.PopulateOnDemand || node.Populated);
1370                                 
1371                         bool hasChildNodes;
1372                         
1373                         if (renderChildNodes)
1374                                 hasChildNodes = node.ChildNodes.Count > 0;
1375                         else
1376                                 hasChildNodes = (node.PopulateOnDemand && !node.Populated) || node.ChildNodes.Count > 0;
1377                                 
1378                         writer.AddAttribute (HtmlTextWriterAttribute.Cellpadding, "0", false);
1379                         writer.AddAttribute (HtmlTextWriterAttribute.Cellspacing, "0", false);
1380                         writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0");
1381                         writer.RenderBeginTag (HtmlTextWriterTag.Table);
1382
1383                         Unit nodeSpacing = GetNodeSpacing (node);
1384
1385                         if (nodeSpacing != Unit.Empty && (node.Depth > 0 || node.Index > 0))
1386                                 RenderMenuItemSpacing (writer, nodeSpacing);
1387                         
1388                         writer.RenderBeginTag (HtmlTextWriterTag.Tr);
1389                         
1390                         // Vertical lines from previous levels
1391
1392                         nodeImage = GetNodeImageUrl ("i", imageStyle);
1393                         for (int n=0; n<level; n++) {
1394                                 writer.RenderBeginTag (HtmlTextWriterTag.Td);
1395                                 writer.AddStyleAttribute (HtmlTextWriterStyle.Width, NodeIndent + "px");
1396                                 writer.AddStyleAttribute (HtmlTextWriterStyle.Height, "1px");
1397                                 writer.RenderBeginTag (HtmlTextWriterTag.Div);
1398                                 if (ShowLines && levelLines [n] != null) {
1399                                         writer.AddAttribute (HtmlTextWriterAttribute.Src, nodeImage);
1400                                         writer.AddAttribute (HtmlTextWriterAttribute.Alt, String.Empty, false);
1401                                         writer.RenderBeginTag (HtmlTextWriterTag.Img);
1402                                         writer.RenderEndTag ();
1403                                 }
1404                                 writer.RenderEndTag ();
1405                                 writer.RenderEndTag (); // TD
1406                         }
1407                         
1408                         // Node image + line
1409                         bool showExpandCollapse = ShowExpandCollapse;
1410                         bool showLines = ShowLines;
1411                         if (showExpandCollapse || showLines) {
1412                                 bool buttonImage = false;
1413                                 string tooltip = String.Empty;
1414                                 string shape = String.Empty;
1415                                 
1416                                 if (showLines) {
1417                                         if (hasPrevious && hasNext)
1418                                                 shape = "t";
1419                                         else if (hasPrevious && !hasNext)
1420                                                 shape = "l";
1421                                         else if (!hasPrevious && hasNext)
1422                                                 shape = "r";
1423                                         else
1424                                                 shape = "dash";
1425                                 }
1426                                 
1427                                 if (showExpandCollapse) {
1428                                         if (hasChildNodes) {
1429                                                 buttonImage = true;
1430                                                 if (node.Expanded.HasValue && node.Expanded.Value)
1431                                                         shape += "minus";
1432                                                 else
1433                                                         shape += "plus";
1434                                                 tooltip = GetNodeImageToolTip (!(node.Expanded.HasValue && node.Expanded.Value), node.Text);
1435                                         } else if (!showLines)
1436                                                 shape = "noexpand";
1437                                 }
1438
1439                                 if (!String.IsNullOrEmpty (shape)) {
1440                                         nodeImage = GetNodeImageUrl (shape, imageStyle);
1441                                         writer.RenderBeginTag (HtmlTextWriterTag.Td);   // TD
1442                                         
1443                                         if (buttonImage) {
1444                                                 if (!clientExpand || (!PopulateNodesFromClient && node.PopulateOnDemand && !node.Populated))
1445                                                         writer.AddAttribute (HtmlTextWriterAttribute.Href, GetClientEvent (node, "ec"));
1446                                                 else
1447                                                         writer.AddAttribute (HtmlTextWriterAttribute.Href, GetClientExpandEvent(node));
1448                                                 writer.RenderBeginTag (HtmlTextWriterTag.A);    // Anchor
1449                                         }
1450
1451                                         // tooltip is 'HtmlAttributeEncoded'
1452                                         writer.AddAttribute (HtmlTextWriterAttribute.Alt, tooltip);
1453
1454                                         if (buttonImage && clientExpand)
1455                                                 writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, "img"));
1456                                         writer.AddAttribute (HtmlTextWriterAttribute.Src, nodeImage);
1457                                         if (buttonImage)
1458                                                 writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0");
1459                                         writer.RenderBeginTag (HtmlTextWriterTag.Img);
1460                                         writer.RenderEndTag ();
1461                                         
1462                                         if (buttonImage)
1463                                                 writer.RenderEndTag ();         // Anchor
1464
1465                                         writer.RenderEndTag ();         // TD
1466                                 }
1467                         }
1468                         
1469                         // Node icon
1470                         
1471                         string imageUrl = node.ImageUrl.Length > 0 ? ResolveClientUrl (node.ImageUrl) : null;
1472                         if (String.IsNullOrEmpty (imageUrl) && imageStyle != null) {
1473                                 if (imageStyle.RootIcon != null && node.IsRootNode)
1474                                         imageUrl = GetNodeIconUrl (imageStyle.RootIcon);
1475                                 else if (imageStyle.ParentIcon != null && node.IsParentNode)
1476                                         imageUrl = GetNodeIconUrl (imageStyle.ParentIcon);
1477                                 else if (imageStyle.LeafIcon != null && node.IsLeafNode)
1478                                         imageUrl = GetNodeIconUrl (imageStyle.LeafIcon);
1479                         }
1480                         
1481                         if (level < LevelStyles.Count && LevelStyles [level].ImageUrl != null)
1482                                 imageUrl = ResolveClientUrl (LevelStyles [level].ImageUrl);
1483                         
1484                         if (!String.IsNullOrEmpty (imageUrl)) {
1485                                 writer.RenderBeginTag (HtmlTextWriterTag.Td);   // TD
1486                                 BeginNodeTag (writer, node, clientExpand);
1487                                 writer.AddAttribute (HtmlTextWriterAttribute.Src, imageUrl);
1488                                 writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0");
1489                                 writer.AddAttribute (HtmlTextWriterAttribute.Alt, node.ImageToolTip);
1490                                 writer.RenderBeginTag (HtmlTextWriterTag.Img);
1491                                 writer.RenderEndTag (); // IMG
1492                                 writer.RenderEndTag (); // style tag
1493                                 writer.RenderEndTag (); // TD
1494                         }
1495
1496                         if (!NodeWrap)
1497                                 writer.AddStyleAttribute (HtmlTextWriterStyle.WhiteSpace, "nowrap");
1498
1499                         bool nodeIsSelected = node == SelectedNode && selectedNodeStyle != null;
1500                         if (!nodeIsSelected && selectedNodeStyle != null) {
1501                                 if (!String.IsNullOrEmpty (activeSiteMapPath))
1502                                         nodeIsSelected = String.Compare (activeSiteMapPath, node.NavigateUrl, RuntimeHelpers.StringComparison) == 0;
1503                         }
1504                         
1505                         AddNodeStyle (writer, node, level, nodeIsSelected);
1506                         if (EnableClientScript) {
1507                                 writer.AddAttribute ("onmouseout", "TreeView_UnhoverNode(this)", false);
1508                                 writer.AddAttribute ("onmouseover", "TreeView_HoverNode('" + ClientID + "', this)");
1509                         }
1510                         writer.RenderBeginTag (HtmlTextWriterTag.Td);   // TD
1511                         
1512                         // Checkbox
1513                         
1514                         if (node.ShowCheckBoxInternal) {
1515                                 writer.AddAttribute (HtmlTextWriterAttribute.Name, ClientID + "_cs_" + node.Path);
1516                                 writer.AddAttribute (HtmlTextWriterAttribute.Type, "checkbox", false);
1517                                 writer.AddAttribute (HtmlTextWriterAttribute.Title, node.Text);
1518                                 if (node.Checked) writer.AddAttribute (HtmlTextWriterAttribute.Checked, "checked", false);
1519                                 writer.RenderBeginTag (HtmlTextWriterTag.Input);        // INPUT
1520                                 writer.RenderEndTag (); // INPUT
1521                         }
1522                         
1523                         // Text
1524                         
1525                         node.BeginRenderText (writer);
1526                         
1527                         if (clientExpand)
1528                                 writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, "txt"));
1529                         AddNodeLinkStyle (writer, node, level, nodeIsSelected);
1530                         BeginNodeTag (writer, node, clientExpand);
1531                         writer.Write (node.Text);
1532                         writer.RenderEndTag (); // style tag
1533                         
1534                         node.EndRenderText (writer);
1535                         
1536                         writer.RenderEndTag (); // TD
1537                         
1538                         writer.RenderEndTag (); // TR
1539
1540                         if (nodeSpacing != Unit.Empty)
1541                                 RenderMenuItemSpacing (writer, nodeSpacing);
1542                         
1543                         writer.RenderEndTag (); // TABLE
1544                         
1545                         // Children
1546                         
1547                         if (hasChildNodes) {
1548                                 if (level >= levelLines.Count) {
1549                                         if (hasNext)
1550                                                 levelLines.Add (this);
1551                                         else
1552                                                 levelLines.Add (null);
1553                                 } else {
1554                                         if (hasNext)
1555                                                 levelLines [level] = this;
1556                                         else
1557                                                 levelLines [level] = null;
1558                                 }
1559                                 
1560                                 if (clientExpand) {
1561                                         if (!(node.Expanded.HasValue && node.Expanded.Value))
1562                                                 writer.AddStyleAttribute (HtmlTextWriterStyle.Display, "none");
1563                                         else
1564                                                 writer.AddStyleAttribute (HtmlTextWriterStyle.Display, "block");
1565                                         writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, null));
1566                                         writer.RenderBeginTag (HtmlTextWriterTag.Span);
1567                                         
1568                                         if (renderChildNodes) {
1569                                                 AddChildrenPadding (writer, node);
1570                                                 int num = node.ChildNodes.Count;
1571                                                 for (int n=0; n<num; n++)
1572                                                         RenderNode (writer, node.ChildNodes [n], level + 1, levelLines, true, n<num-1);
1573                                                 if (hasNext)
1574                                                         AddChildrenPadding (writer, node);
1575                                         }
1576                                         writer.RenderEndTag (); // SPAN
1577                                 } else if (renderChildNodes) {
1578                                         AddChildrenPadding (writer, node);
1579                                         int num = node.ChildNodes.Count;
1580                                         for (int n=0; n<num; n++)
1581                                                 RenderNode (writer, node.ChildNodes [n], level + 1, levelLines, true, n<num-1);
1582                                         if (hasNext)
1583                                                 AddChildrenPadding (writer, node);
1584                                 }
1585                         }
1586                 }
1587
1588                 void AddChildrenPadding (HtmlTextWriter writer, TreeNode node)
1589                 {
1590                         int level = node.Depth;
1591                         Unit cnp = Unit.Empty;
1592                         
1593                         if (levelStyles != null && level < levelStyles.Count)
1594                                 cnp = levelStyles [level].ChildNodesPadding;
1595                         if (cnp.IsEmpty && nodeStyle != null)
1596                                 cnp = nodeStyle.ChildNodesPadding;
1597                         
1598                         double value;
1599                         if (cnp.IsEmpty || (value = cnp.Value) == 0 || cnp.Type != UnitType.Pixel)
1600                                 return;
1601
1602                         writer.RenderBeginTag (HtmlTextWriterTag.Table);
1603                         writer.AddAttribute (HtmlTextWriterAttribute.Height, ((int) value).ToString (), false);
1604                         writer.RenderBeginTag (HtmlTextWriterTag.Tr);
1605                         writer.RenderBeginTag (HtmlTextWriterTag.Td);
1606                         writer.RenderEndTag (); // td
1607                         writer.RenderEndTag (); // tr
1608                         writer.RenderEndTag (); // table
1609                 }
1610                 
1611                 void RenderMenuItemSpacing (HtmlTextWriter writer, Unit itemSpacing)
1612                 {
1613                         writer.AddStyleAttribute (HtmlTextWriterStyle.Height, itemSpacing.ToString ());
1614                         writer.RenderBeginTag (HtmlTextWriterTag.Tr);
1615                         writer.RenderBeginTag (HtmlTextWriterTag.Td);
1616                         writer.RenderEndTag ();
1617                         writer.RenderEndTag ();
1618                 }
1619
1620                 Unit GetNodeSpacing (TreeNode node)
1621                 {
1622                         if (node.Selected && selectedNodeStyle != null && selectedNodeStyle.NodeSpacing != Unit.Empty)
1623                                 return selectedNodeStyle.NodeSpacing;
1624
1625                         if (levelStyles != null && node.Depth < levelStyles.Count && levelStyles [node.Depth].NodeSpacing != Unit.Empty)
1626                                 return levelStyles [node.Depth].NodeSpacing;
1627
1628                         if (node.IsLeafNode) {
1629                                 if (leafNodeStyle != null && leafNodeStyle.NodeSpacing != Unit.Empty)
1630                                         return leafNodeStyle.NodeSpacing;
1631                         } else if (node.IsRootNode) {
1632                                 if (rootNodeStyle != null && rootNodeStyle.NodeSpacing != Unit.Empty)
1633                                         return rootNodeStyle.NodeSpacing;
1634                         } else if (node.IsParentNode) {
1635                                 if (parentNodeStyle != null && parentNodeStyle.NodeSpacing != Unit.Empty)
1636                                         return parentNodeStyle.NodeSpacing;
1637                         }
1638
1639                         if (nodeStyle != null)
1640                                 return nodeStyle.NodeSpacing;
1641                         else
1642                                 return Unit.Empty;
1643                 }
1644
1645                 void AddNodeStyle (HtmlTextWriter writer, TreeNode node, int level, bool nodeIsSelected)
1646                 {
1647                         TreeNodeStyle style = new TreeNodeStyle ();
1648                         if (Page.Header != null) {
1649                                 // styles are registered
1650                                 if (nodeStyle != null) {
1651                                         style.AddCssClass (nodeStyle.CssClass);
1652                                         style.AddCssClass (nodeStyle.RegisteredCssClass);
1653                                 }
1654                                 if (node.IsLeafNode) {
1655                                         if (leafNodeStyle != null) {
1656                                                 style.AddCssClass (leafNodeStyle.CssClass);
1657                                                 style.AddCssClass (leafNodeStyle.RegisteredCssClass);
1658                                         }
1659                                 } else if (node.IsRootNode) {
1660                                         if (rootNodeStyle != null) {
1661                                                 style.AddCssClass (rootNodeStyle.CssClass);
1662                                                 style.AddCssClass (rootNodeStyle.RegisteredCssClass);
1663                                         }
1664                                 } else if (node.IsParentNode) {
1665                                         if (parentNodeStyle != null) {
1666                                                 style.AddCssClass (parentNodeStyle.CssClass);
1667                                                 style.AddCssClass (parentNodeStyle.RegisteredCssClass);
1668                                         }
1669                                 }
1670                                 if (levelStyles != null && levelStyles.Count > level) {
1671                                         style.AddCssClass (levelStyles [level].CssClass);
1672                                         style.AddCssClass (levelStyles [level].RegisteredCssClass);
1673                                 }
1674                                 
1675                                 if (nodeIsSelected) {
1676                                         style.AddCssClass (selectedNodeStyle.CssClass);
1677                                         style.AddCssClass (selectedNodeStyle.RegisteredCssClass);
1678                                 }
1679                         } else {
1680                                 // styles are not registered
1681                                 if (nodeStyle != null) {
1682                                         style.CopyFrom (nodeStyle);
1683                                 }
1684                                 if (node.IsLeafNode) {
1685                                         if (leafNodeStyle != null) {
1686                                                 style.CopyFrom (leafNodeStyle);
1687                                         }
1688                                 } else if (node.IsRootNode) {
1689                                         if (rootNodeStyle != null) {
1690                                                 style.CopyFrom (rootNodeStyle);
1691                                         }
1692                                 } else if (node.IsParentNode) {
1693                                         if (parentNodeStyle != null) {
1694                                                 style.CopyFrom (parentNodeStyle);
1695                                         }
1696                                 }
1697                                 if (levelStyles != null && levelStyles.Count > level)
1698                                         style.CopyFrom (levelStyles [level]);
1699                                 
1700                                 if (nodeIsSelected)
1701                                         style.CopyFrom (selectedNodeStyle);
1702                         }
1703                         style.AddAttributesToRender (writer);
1704                 }
1705
1706                 void AddNodeLinkStyle (HtmlTextWriter writer, TreeNode node, int level, bool nodeIsSelected)
1707                 {
1708                         Style style = new Style ();
1709                         if (Page.Header != null) {
1710                                 // styles are registered
1711                                 style.AddCssClass (ControlLinkStyle.RegisteredCssClass);
1712
1713                                 if (nodeStyle != null) {
1714                                         style.AddCssClass (nodeLinkStyle.CssClass);
1715                                         style.AddCssClass (nodeLinkStyle.RegisteredCssClass);
1716                                 }
1717
1718                                 if (levelLinkStyles != null && levelLinkStyles.Count > level) {
1719                                         style.AddCssClass (levelLinkStyles [level].CssClass);
1720                                         style.AddCssClass (levelLinkStyles [level].RegisteredCssClass);
1721                                 }
1722                                 
1723                                 if (node.IsLeafNode) {
1724                                         if (leafNodeStyle != null) {
1725                                                 style.AddCssClass (leafNodeLinkStyle.CssClass);
1726                                                 style.AddCssClass (leafNodeLinkStyle.RegisteredCssClass);
1727                                         }
1728                                 } else if (node.IsRootNode) {
1729                                         if (rootNodeStyle != null) {
1730                                                 style.AddCssClass (rootNodeLinkStyle.CssClass);
1731                                                 style.AddCssClass (rootNodeLinkStyle.RegisteredCssClass);
1732                                         }
1733                                 } else if (node.IsParentNode) {
1734                                         if (parentNodeStyle != null) {
1735                                                 style.AddCssClass (parentNodeLinkStyle.CssClass);
1736                                                 style.AddCssClass (parentNodeLinkStyle.RegisteredCssClass);
1737                                         }
1738                                 }
1739                                 
1740                                 if (nodeIsSelected) {
1741                                         style.AddCssClass (selectedNodeLinkStyle.CssClass);
1742                                         style.AddCssClass (selectedNodeLinkStyle.RegisteredCssClass);
1743                                 }
1744                         } else {
1745                                 // styles are not registered
1746                                 style.CopyFrom (ControlLinkStyle);
1747                                 if (nodeStyle != null)
1748                                         style.CopyFrom (nodeLinkStyle);
1749
1750                                 if (levelLinkStyles != null && levelLinkStyles.Count > level)
1751                                         style.CopyFrom (levelLinkStyles [level]);
1752                                 
1753                                 if (node.IsLeafNode) {
1754                                         if (node.IsLeafNode && leafNodeStyle != null) {
1755                                                 style.CopyFrom (leafNodeLinkStyle);
1756                                         }
1757                                 } else if (node.IsRootNode) {
1758                                         if (node.IsRootNode && rootNodeStyle != null) {
1759                                                 style.CopyFrom (rootNodeLinkStyle);
1760                                         }
1761                                 } else if (node.IsParentNode) {
1762                                         if (node.IsParentNode && parentNodeStyle != null) {
1763                                                 style.CopyFrom (parentNodeLinkStyle);
1764                                         }
1765                                 }
1766                                 
1767                                 if (nodeIsSelected)
1768                                         style.CopyFrom (selectedNodeLinkStyle);
1769                                 style.AlwaysRenderTextDecoration = true;
1770                         }
1771                         style.AddAttributesToRender (writer);
1772                 }
1773
1774                 void BeginNodeTag (HtmlTextWriter writer, TreeNode node, bool clientExpand)
1775                 {
1776                         if(node.ToolTip.Length>0)
1777                                 writer.AddAttribute (HtmlTextWriterAttribute.Title, node.ToolTip);
1778
1779                         string navigateUrl = node.NavigateUrl;
1780                         if (!String.IsNullOrEmpty (navigateUrl)) {
1781                                 string target = node.Target.Length > 0 ? node.Target : Target;
1782 #if TARGET_J2EE
1783                                 string navUrl = ResolveClientUrl (navigateUrl, String.Compare (target, "_blank", StringComparison.InvariantCultureIgnoreCase) != 0);
1784 #else
1785                                 string navUrl = ResolveClientUrl (navigateUrl);
1786 #endif
1787                                 writer.AddAttribute (HtmlTextWriterAttribute.Href, navUrl);
1788                                 if (target.Length > 0)
1789                                         writer.AddAttribute (HtmlTextWriterAttribute.Target, target);
1790                                 writer.RenderBeginTag (HtmlTextWriterTag.A);
1791                         } else if (node.SelectAction != TreeNodeSelectAction.None) {
1792                                 if (node.SelectAction == TreeNodeSelectAction.Expand && clientExpand)
1793                                         writer.AddAttribute (HtmlTextWriterAttribute.Href, GetClientExpandEvent (node));
1794                                 else
1795                                         writer.AddAttribute (HtmlTextWriterAttribute.Href, GetClientEvent (node, "sel"));
1796                                 writer.RenderBeginTag (HtmlTextWriterTag.A);
1797                         } else
1798                                 writer.RenderBeginTag (HtmlTextWriterTag.Span);
1799                 }
1800                 
1801                 string GetNodeImageToolTip (bool expand, string txt)
1802                 {
1803                         if (expand) {
1804                                 string expandImageToolTip = ExpandImageToolTip;
1805                                 if (!String.IsNullOrEmpty (expandImageToolTip))
1806                                         return String.Format (expandImageToolTip, HttpUtility.HtmlAttributeEncode (txt));
1807                                 else if (txt != null)
1808                                         return "Expand " + HttpUtility.HtmlAttributeEncode (txt);
1809                                 else
1810                                         return "Expand {0}";
1811                         } else {
1812                                 string collapseImageToolTip = CollapseImageToolTip;
1813                                 if (!String.IsNullOrEmpty (collapseImageToolTip))
1814                                         return String.Format (collapseImageToolTip, HttpUtility.HtmlAttributeEncode (txt));
1815                                 else if (txt != null)
1816                                         return "Collapse " + HttpUtility.HtmlAttributeEncode (txt);
1817                                 else
1818                                         return "Collapse {0}";
1819                         }
1820                 }
1821                 
1822                 string GetNodeClientId (TreeNode node, string sufix)
1823                 {
1824                         return ClientID + "_" + node.Path + (sufix != null ? "_" + sufix : String.Empty);
1825                 }
1826                                                         
1827                 string GetNodeImageUrl (string shape, ImageStyle imageStyle)
1828                 {
1829                         if (ShowLines) {
1830                                 if (!String.IsNullOrEmpty (LineImagesFolder))
1831                                         return ResolveClientUrl (LineImagesFolder + "/" + shape + ".gif");
1832                         } else {
1833                                 if (imageStyle != null) {
1834                                         if (shape == "plus") {
1835                                                 if (!String.IsNullOrEmpty (imageStyle.Expand))
1836                                                         return GetNodeIconUrl (imageStyle.Expand);
1837                                         } else if (shape == "minus") {
1838                                                 if (!String.IsNullOrEmpty (imageStyle.Collapse))
1839                                                         return GetNodeIconUrl (imageStyle.Collapse);
1840                                         } else if (shape == "noexpand") {
1841                                                 if (!String.IsNullOrEmpty (imageStyle.NoExpand))
1842                                                         return GetNodeIconUrl (imageStyle.NoExpand);
1843                                         }
1844                                 } else {
1845                                         if (shape == "plus") {
1846                                                 if (!String.IsNullOrEmpty (ExpandImageUrl))
1847                                                         return ResolveClientUrl (ExpandImageUrl);
1848                                         } else if (shape == "minus") {
1849                                                 if (!String.IsNullOrEmpty (CollapseImageUrl))
1850                                                         return ResolveClientUrl (CollapseImageUrl);
1851                                         } else if (shape == "noexpand") {
1852                                                 if (!String.IsNullOrEmpty (NoExpandImageUrl))
1853                                                         return ResolveClientUrl (NoExpandImageUrl);
1854                                         }
1855                                 }
1856                                 if (!String.IsNullOrEmpty (LineImagesFolder))
1857                                         return ResolveClientUrl (LineImagesFolder + "/" + shape + ".gif");
1858                         }
1859                         return Page.ClientScript.GetWebResourceUrl (typeof (TreeView), "TreeView_" + shape + ".gif");
1860                 }
1861                 
1862                 string GetNodeIconUrl (string icon)
1863                 {
1864                         return Page.ClientScript.GetWebResourceUrl (typeof (TreeView), icon + ".gif");
1865                 }
1866                 
1867                 string GetClientEvent (TreeNode node, string ev)
1868                 {
1869                         return Page.ClientScript.GetPostBackClientHyperlink (this, ev + "|" + node.Path, true);
1870                 }
1871                 
1872                 string GetClientExpandEvent (TreeNode node)
1873                 {
1874                         return String.Format ("javascript:TreeView_ToggleExpand ('{0}','{1}','{2}','{3}','{4}','{5}')",
1875                                               ClientID,
1876                                               node.Path,
1877                                               HttpUtility.HtmlAttributeEncode (node.Value).Replace ("'", "\\'").Replace ("|","U+007C"),
1878                                               HttpUtility.HtmlAttributeEncode (node.ImageUrl).Replace ("'", "\\'").Replace ("|","U+007c"),
1879                                               HttpUtility.HtmlAttributeEncode (node.NavigateUrl).Replace ("'", "\\'").Replace ("|","U+007C"),
1880                                               HttpUtility.HtmlAttributeEncode (node.Target).Replace ("'", "\\'").Replace ("|","U+007C"));
1881                 }
1882
1883                 TreeNode FindNodeByPos (string path)
1884                 {
1885                         string[] indexes = path.Split ('_');
1886                         TreeNode node = null;
1887                         
1888                         foreach (string index in indexes) {
1889                                 int i = int.Parse (index);
1890                                 if (node == null) {
1891                                         if (i >= Nodes.Count) return null;
1892                                         node = Nodes [i];
1893                                 } else {
1894                                         if (i >= node.ChildNodes.Count) return null;
1895                                         node = node.ChildNodes [i];
1896                                 }
1897                         }
1898                         return node;
1899                 }
1900                 
1901                 void UnsetCheckStates (TreeNodeCollection col, NameValueCollection states)
1902                 {
1903                         foreach (TreeNode node in col) {
1904                                 if (node.ShowCheckBoxInternal && node.Checked) {
1905                                         if (states == null || states [ClientID + "_cs_" + node.Path] == null)
1906                                                 node.Checked = false;
1907                                 }
1908                                 if (node.HasChildData)
1909                                         UnsetCheckStates (node.ChildNodes, states);
1910                         }
1911                 }
1912                 
1913                 void SetCheckStates (NameValueCollection states)
1914                 {
1915                         if (states == null)
1916                                 return;
1917
1918                         string keyPrefix = ClientID + "_cs_";
1919                         foreach (string key in states) {
1920                                 if (key.StartsWith (keyPrefix, StringComparison.Ordinal)) {
1921                                         string id = key.Substring (keyPrefix.Length);
1922                                         TreeNode node = FindNodeByPos (id);
1923                                         if (node != null && !node.Checked)
1924                                                 node.Checked = true;
1925                                 }
1926                         }
1927                 }
1928                 
1929                 void UnsetExpandStates (TreeNodeCollection col, string[] states)
1930                 {
1931                         foreach (TreeNode node in col) {
1932                                 if (node.Expanded.HasValue && node.Expanded.Value) {
1933                                         bool expand = (Array.IndexOf (states, node.Path) != -1);
1934                                         if (!expand) node.Expanded = false;
1935                                 }
1936                                 if (node.HasChildData)
1937                                         UnsetExpandStates (node.ChildNodes, states);
1938                         }
1939                 }
1940                 
1941                 void SetExpandStates (string[] states)
1942                 {
1943                         foreach (string id in states) {
1944                                 if (String.IsNullOrEmpty (id))
1945                                         continue;
1946                                 TreeNode node = FindNodeByPos (id);
1947                                 if (node != null)
1948                                         node.Expanded = true;
1949                         }
1950                 }
1951                 
1952                 string GetExpandStates ()
1953                 {
1954                         StringBuilder sb = new StringBuilder ("|");
1955                         
1956                         foreach (TreeNode node in Nodes)
1957                                 GetExpandStates (sb, node);
1958
1959                         return sb.ToString ();
1960                 }
1961                 
1962                 void GetExpandStates (StringBuilder sb, TreeNode node)
1963                 {
1964                         if (node.Expanded.HasValue && node.Expanded.Value) {
1965                                 sb.Append (node.Path);
1966                                 sb.Append ('|');
1967                         }
1968                         if (node.HasChildData) {
1969                                 foreach (TreeNode child in node.ChildNodes)
1970                                         GetExpandStates (sb, child);
1971                         }
1972                 }
1973         }
1974 }
1975 #endif