New test.
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / TreeNode.cs
1 //
2 // System.Web.UI.WebControls.TreeNode.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;
34 using System.Collections;
35 using System.Text;
36 using System.ComponentModel;
37 using System.Web.UI;
38
39 namespace System.Web.UI.WebControls
40 {
41         [ParseChildrenAttribute (true, "ChildNodes")]
42         public class TreeNode: IStateManager, ICloneable
43         {
44                 StateBag ViewState = new StateBag ();
45                 TreeNodeCollection nodes;
46                 bool marked;
47                 TreeView tree;
48                 TreeNode parent;
49                 int index;
50                 string path;
51                 int depth = -1;
52                 
53                 object dataItem;
54                 IHierarchyData hierarchyData;
55
56                 bool gotBinding;
57                 TreeNodeBinding binding;
58                 PropertyDescriptorCollection boundProperties;
59                 
60                 internal TreeNode (TreeView tree)
61                 {
62                         Tree = tree;
63                 }
64                 
65                 public TreeNode ()
66                 {
67                 }
68                 
69                 public TreeNode (string text)
70                 {
71                         Text = text;
72                 }
73                 
74                 public TreeNode (string text, string value)
75                 {
76                         Text = text;
77                         Value = value;
78                 }
79                 
80                 public TreeNode (string text, string value, string imageUrl)
81                 {
82                         Text = text;
83                         Value = value;
84                         ImageUrl = imageUrl;
85                 }
86                 
87                 public TreeNode (string text, string value, string imageUrl, string navigateUrl, string target)
88                 {
89                         Text = text;
90                         Value = value;
91                         ImageUrl = imageUrl;
92                         NavigateUrl = navigateUrl;
93                         Target = target;
94                 }
95                 
96                 [MonoTODO ("Not implemented")]
97                 protected TreeNode (TreeView owner, bool isRoot)
98                 {
99                         throw new NotImplementedException ();
100                 }
101
102                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
103                 [Browsable (false)]
104                 public int Depth {
105                         get {
106                                 if (depth != -1) return depth;
107                                 depth = 0;
108                                 TreeNode nod = parent;
109                                 while (nod != null) {
110                                         depth++;
111                                         nod = nod.parent;
112                                 }
113                                 return depth;
114                         }
115                 }
116                 
117                 void ResetPathData ()
118                 {
119                         path = null;
120                         depth = -1;
121                         gotBinding = false;
122                 }
123                 
124                 internal TreeView Tree {
125                         get { return tree; }
126                         set {
127                                 if (SelectedFlag) {
128                                         if (value != null)
129                                                 value.SetSelectedNode (this, false);
130                                         else if (tree != null)
131                                                 tree.SetSelectedNode (null, false);
132                                 }
133                                 tree = value;
134                                 if (nodes != null)
135                                         nodes.SetTree (tree);
136                                 ResetPathData ();
137                                 if (PopulateOnDemand && !Populated && Expanded.HasValue && Expanded.Value)
138                                         Populate ();
139                         }
140                 }
141                 
142                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
143                 [DefaultValue (false)]
144                 [Browsable (false)]
145                 public bool DataBound {
146                         get { return ViewState ["DataBound"] == null ? false : (bool) ViewState ["DataBound"]; }
147                         private set { ViewState ["DataBound"] = value; }
148                 }
149                 
150                 [DefaultValue (null)]
151                 [Browsable (false)]
152                 public object DataItem {
153                         get {
154                                 return dataItem;
155                         }
156                 }
157                 
158                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
159                 [DefaultValue ("")]
160                 [Browsable (false)]
161                 public string DataPath {
162                         get { return ViewState ["DataPath"] == null ? String.Empty : (String) ViewState ["DataPath"]; }
163                         private set { ViewState ["DataPath"] = value; }
164                 }
165                 
166                 [DefaultValue (false)]
167                 public bool Checked {
168                         get {
169                                 object o = ViewState ["Checked"];
170                                 if (o != null) return (bool)o;
171                                 return false;
172                         }
173                         set {
174                                 ViewState ["Checked"] = value;
175                                 if (tree != null)
176                                         tree.NotifyCheckChanged (this);
177                         }
178                 }
179
180                 [DefaultValue (null)]
181                 [MergableProperty (false)]
182                 [Browsable (false)]
183                 [PersistenceMode (PersistenceMode.InnerDefaultProperty)]
184                 public TreeNodeCollection ChildNodes {
185                         get {
186                                 if (nodes == null) {
187                                         nodes = new TreeNodeCollection (this);
188                                                 
189                                         if (IsTrackingViewState)
190                                                 ((IStateManager)nodes).TrackViewState();
191                                 }
192                                 return nodes;
193                         }
194                 }
195                 
196                 [DefaultValue (null)]
197                 public bool? Expanded {
198                         get {
199                                 object o = ViewState ["Expanded"];
200                                 return (bool?)o;
201                         }
202                         set {
203                                 ViewState ["Expanded"] = value;
204                                 if (tree != null)
205                                         tree.NotifyExpandedChanged (this);
206                                 if (PopulateOnDemand && !Populated && value.HasValue && value.Value)
207                                         Populate ();
208                         }
209                 }
210
211                 [Localizable (true)]
212                 [DefaultValue ("")]
213                 public string ImageToolTip {
214                         get {
215                                 object o = ViewState ["ImageToolTip"];
216                                 if (o != null) return (string)o;
217                                 return "";
218                         }
219                         set {
220                                 ViewState ["ImageToolTip"] = value;
221                         }
222                 }
223                 
224                 [DefaultValue ("")]
225                 [UrlProperty]
226                 [Editor ("System.Web.UI.Design.ImageUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
227                 public string ImageUrl {
228                         get {
229                                 object o = ViewState ["ImageUrl"];
230                                 if (o != null) return (string)o;
231                                 return "";
232                         }
233                         set {
234                                 ViewState ["ImageUrl"] = value;
235                         }
236                 }
237
238                 [DefaultValue ("")]
239                 [UrlProperty]
240                 [Editor ("System.Web.UI.Design.UrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
241                 public string NavigateUrl {
242                         get {
243                                 object o = ViewState ["NavigateUrl"];
244                                 if (o != null) return (string)o;
245                                 return "";
246                         }
247                         set {
248                                 ViewState ["NavigateUrl"] = value;
249                         }
250                 }
251
252                 [DefaultValue (false)]
253                 public bool PopulateOnDemand {
254                         get {
255                                 object o = ViewState ["PopulateOnDemand"];
256                                 if (o != null) return (bool)o;
257                                 return false;
258                         }
259                         set {
260                                 ViewState ["PopulateOnDemand"] = value;
261                         }
262                 }
263
264                 [DefaultValue (TreeNodeSelectAction.Select)]
265                 public TreeNodeSelectAction SelectAction {
266                         get {
267                                 object o = ViewState ["SelectAction"];
268                                 if (o != null) return (TreeNodeSelectAction)o;
269                                 return TreeNodeSelectAction.Select;
270                         }
271                         set {
272                                 ViewState ["SelectAction"] = value;
273                         }
274                 }
275
276                 [DefaultValue (null)]
277                 public bool? ShowCheckBox {
278                         get {
279                                 object o = ViewState ["ShowCheckBox"];
280                                 return (bool?)o;
281                         }
282                         set {
283                                 ViewState ["ShowCheckBox"] = value;
284                         }
285                 }
286                 
287                 [DefaultValue ("")]
288                 public string Target {
289                         get {
290                                 object o = ViewState ["Target"];
291                                 if(o != null) return (string)o;
292                                 return "";
293                         }
294                         set {
295                                 ViewState ["Target"] = value;
296                         }
297                 }
298
299                 [Localizable (true)]
300                 [DefaultValue ("")]
301                 [WebSysDescription ("The display text of the tree node.")]
302                 public string Text {
303                         get {
304                                 object o = ViewState ["Text"];
305                                 if (o != null) return (string)o;
306                                 return "";
307                         }
308                         set {
309                                 ViewState ["Text"] = value;
310                         }
311                 }
312
313                 [Localizable (true)]
314                 [DefaultValue ("")]
315                 public string ToolTip {
316                         get {
317                                 object o = ViewState ["ToolTip"];
318                                 if(o != null) return (string)o;
319                                 return "";
320                         }
321                         set {
322                                 ViewState ["ToolTip"] = value;
323                         }
324                 }
325
326                 [Localizable (true)]
327                 [DefaultValue ("")]
328                 public string Value {
329                         get {
330                                 object o = ViewState ["Value"];
331                                 if(o != null) return (string)o;
332                                 return "";
333                         }
334                         set {
335                                 ViewState ["Value"] = value;
336                         }
337                 }
338                 
339                 [DefaultValue (false)]
340                 public bool Selected {
341                         get {
342                                 return SelectedFlag;
343                         }
344                         set {
345                                 if (tree != null) {
346                                         if (!value && tree.SelectedNode == this)
347                                                 tree.SetSelectedNode (null, false);
348                                         else if (value)
349                                                 tree.SetSelectedNode (this, false);
350                                 }
351                                 else
352                                         SelectedFlag = value;
353                         }
354                 }
355                 
356                 internal virtual bool SelectedFlag {
357                         get {
358                                 object o = ViewState ["Selected"];
359                                 if(o != null) return (bool)o;
360                                 return false;
361                         }
362                         set {
363                                 ViewState ["Selected"] = value;
364                         }
365                 }
366                 
367                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
368                 [Browsable (false)]
369                 public TreeNode Parent {
370                         get { return parent; }
371                 }
372                 
373                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
374                 [Browsable (false)]
375                 public string ValuePath {
376                         get {
377                                 if (tree == null) return Value;
378                                 
379                                 StringBuilder sb = new StringBuilder (Value);
380                                 TreeNode node = parent;
381                                 while (node != null) {
382                                         sb.Insert (0, tree.PathSeparator);
383                                         sb.Insert (0, node.Value);
384                                         node = node.Parent;
385                                 }
386                                 return sb.ToString ();
387                         }
388                 }
389                 
390                 internal int Index {
391                         get { return index; }
392                         set { index = value; ResetPathData (); }
393                 }
394                 
395                 internal void SetParent (TreeNode node) {
396                         parent = node;
397                         ResetPathData ();
398                 }
399                 
400                 internal string Path {
401                         get {
402                                 if (path != null) return path;
403                                 StringBuilder sb = new StringBuilder (index.ToString());
404                                 TreeNode node = parent;
405                                 while (node != null) {
406                                         sb.Insert (0, '_');
407                                         sb.Insert (0, node.Index.ToString ());
408                                         node = node.Parent;
409                                 }
410                                 path = sb.ToString ();
411                                 return path;
412                         }
413                 }
414                 
415                 internal bool Populated {
416                         get {
417                                 object o = ViewState ["Populated"];
418                                 if (o != null) return (bool) o;
419                                 return false;
420                         }
421                         set {
422                                 ViewState ["Populated"] = value;
423                         }
424                 }
425
426                 internal bool HasChildData {
427                         get { return nodes != null; }
428                 }
429                 
430                 internal void Populate ()
431                 {
432                         if (tree == null)
433                                 return;
434
435                         Populated = true;
436                         tree.NotifyPopulateRequired (this);
437                 }
438                 
439                 public void Collapse ()
440                 {
441                         Expanded = false;
442                 }
443
444                 public void CollapseAll ()
445                 {
446                         SetExpandedRec (false, -1);
447                 }
448
449                 public void Expand ()
450                 {
451                         Expanded = true;
452                 }
453
454                 internal void Expand (int depth)
455                 {
456                         SetExpandedRec (true, depth);
457                 }
458
459                 public void ExpandAll ()
460                 {
461                         SetExpandedRec (true, -1);
462                 }
463                 
464                 void SetExpandedRec (bool expanded, int depth)
465                 {
466                         Expanded = expanded;
467                         if (depth == 0) return;
468                         
469                         foreach (TreeNode nod in ChildNodes)
470                                 nod.SetExpandedRec (expanded, depth - 1);
471                 }
472                 
473                 public void Select ()
474                 {
475                         Selected = true;
476                 }
477                 
478                 public void ToggleExpandState ()
479                 {
480 #if TARGET_JVM //No support for Nullable<bool>.GetValueOrDefault() yet
481                         bool? value = Expanded;
482                         Expanded = value.HasValue ? !value.Value : true;
483 #else
484                         Expanded = !Expanded.GetValueOrDefault(false);
485 #endif
486                 }
487
488                 void IStateManager.LoadViewState (object savedState)
489                 {
490                         LoadViewState (savedState);
491                 }
492
493                 protected virtual void LoadViewState (object savedState)
494                 {
495                         if (savedState == null)
496                                 return;
497
498                         object[] states = (object[]) savedState;
499                         ViewState.LoadViewState (states [0]);
500                         
501                         if (tree != null && SelectedFlag)
502                                 tree.SetSelectedNode (this, true);
503                         
504                         if (!PopulateOnDemand || Populated)
505                                 ((IStateManager)ChildNodes).LoadViewState (states [1]);
506                 }
507                 
508                 object IStateManager.SaveViewState ()
509                 {
510                         return SaveViewState ();
511                 }
512
513                 protected virtual object SaveViewState ()
514                 {
515                         object[] states = new object[2];
516                         states[0] = ViewState.SaveViewState();
517                         states[1] = (nodes == null ? null : ((IStateManager)nodes).SaveViewState());
518                         
519                         for (int i = 0; i < states.Length; i++) {
520                                 if (states [i] != null)
521                                         return states;
522                         }
523                         return null;
524                 }
525
526                 void IStateManager.TrackViewState ()
527                 {
528                         TrackViewState ();
529                 }
530
531                 protected void TrackViewState ()
532                 {
533                         if (marked) return;
534                         marked = true;
535                         ViewState.TrackViewState();
536
537                         if (nodes != null)
538                                 ((IStateManager)nodes).TrackViewState ();
539                 }
540                 
541                 bool IStateManager.IsTrackingViewState {
542                         get { return IsTrackingViewState; }
543                 }
544
545                 protected bool IsTrackingViewState
546                 {
547                         get { return marked; }
548                 }
549                 
550                 internal void SetDirty ()
551                 {
552                         ViewState.SetDirty (true);
553                         if (nodes != null)
554                                 nodes.SetDirty ();
555                 }
556                 
557                 public virtual object Clone ()
558                 {
559                         TreeNode nod = tree != null ? tree.CreateNode () : new TreeNode ();
560                         foreach (DictionaryEntry e in ViewState)
561                                 nod.ViewState [(string)e.Key] = ((StateItem)e.Value).Value;
562                                 
563                         foreach (TreeNode c in ChildNodes)
564                                 nod.ChildNodes.Add ((TreeNode)c.Clone ());
565                                 
566                         return nod;
567                 }
568                 
569                 internal void Bind (IHierarchyData hierarchyData)
570                 {
571                         this.hierarchyData = hierarchyData;
572                         DataBound = true;
573                         DataPath = hierarchyData.Path;
574                         dataItem = hierarchyData.Item;
575                         
576                         TreeNodeBinding bin = GetBinding ();
577                         if (bin != null) {
578                         
579                                 // Bind ImageToolTip property
580
581                                 if (bin.ImageToolTipField.Length > 0) {
582                                         ImageToolTip = Convert.ToString (GetBoundPropertyValue (bin.ImageToolTipField));
583                                         if (ImageToolTip.Length == 0)
584                                                 ImageToolTip = bin.ImageToolTip;
585                                 }
586                                 else if (bin.ImageToolTip.Length > 0)
587                                         ImageToolTip = bin.ImageToolTip;
588                                         
589                                 // Bind ImageUrl property
590
591                                 if (bin.ImageUrlField.Length > 0) {
592                                         ImageUrl = Convert.ToString (GetBoundPropertyValue (bin.ImageUrlField));
593                                         if (ImageUrl.Length == 0)
594                                                 ImageUrl = bin.ImageUrl;
595                                 }
596                                 else if (bin.ImageUrl.Length > 0)
597                                         ImageUrl = bin.ImageUrl;
598                                         
599                                 // Bind NavigateUrl property
600
601                                 if (bin.NavigateUrlField.Length > 0) {
602                                         NavigateUrl = Convert.ToString (GetBoundPropertyValue (bin.NavigateUrlField));
603                                         if (NavigateUrl.Length == 0)
604                                                 NavigateUrl = bin.NavigateUrl;
605                                 }
606                                 else if (bin.NavigateUrl.Length > 0)
607                                         NavigateUrl = bin.NavigateUrl;
608                                         
609                                 // Bind PopulateOnDemand property
610                                 
611                                 if (bin.HasPropertyValue ("PopulateOnDemand"))
612                                         PopulateOnDemand = bin.PopulateOnDemand;
613                                 
614                                 // Bind SelectAction property
615                                         
616                                 if (bin.HasPropertyValue ("SelectAction"))
617                                         SelectAction = bin.SelectAction;
618                                 
619                                 // Bind ShowCheckBox property
620                                         
621                                 if (bin.HasPropertyValue ("ShowCheckBox"))
622                                         ShowCheckBox = bin.ShowCheckBox;
623                                         
624                                 // Bind Target property
625
626                                 if (bin.TargetField.Length > 0) {
627                                         Target = Convert.ToString (GetBoundPropertyValue (bin.TargetField));
628                                         if (Target.Length == 0)
629                                                 Target = bin.Target;
630                                 }
631                                 else if (bin.Target.Length > 0)
632                                         Target = bin.Target;
633                                         
634                                 // Bind Text property
635                                         
636                                 if (bin.TextField.Length > 0) {
637                                         Text = Convert.ToString (GetBoundPropertyValue (bin.TextField));
638                                         if (bin.FormatString.Length > 0)
639                                                 Text = string.Format (bin.FormatString, Text);
640                                         if (Text.Length == 0)
641                                                 Text = bin.Text;
642                                         if (Text.Length == 0)
643                                                 Text = bin.Value;
644                                         if (Text.Length == 0 && bin.ValueField.Length > 0)
645                                                 Text = Convert.ToString (GetBoundPropertyValue (bin.ValueField));
646                                 }
647                                 else if (bin.Text.Length > 0)
648                                         Text = bin.Text;
649                                 else if (bin.Value.Length > 0)
650                                         Text = bin.Value;
651                                 else
652                                         Text = GetDefaultBoundText ();
653                                         
654                                 // Bind ToolTip property
655
656                                 if (bin.ToolTipField.Length > 0) {
657                                         ToolTip = Convert.ToString (GetBoundPropertyValue (bin.ToolTipField));
658                                         if (ToolTip.Length == 0)
659                                                 ToolTip = bin.ToolTip;
660                                 }
661                                 else if (bin.ToolTip.Length > 0)
662                                         ToolTip = bin.ToolTip;
663                                         
664                                 // Bind Value property
665
666                                 if (bin.ValueField.Length > 0) {
667                                         Value = Convert.ToString (GetBoundPropertyValue (bin.ValueField));
668                                         if (Value.Length == 0)
669                                                 Value = bin.Value;
670                                         if (Value.Length == 0)
671                                                 Value = bin.Text;
672                                         if(Value.Length == 0 && bin.TextField.Length > 0)
673                                                 Value = Convert.ToString (GetBoundPropertyValue (bin.TextField));
674                                 }
675                                 else if (bin.Value.Length > 0)
676                                         Value = bin.Value;
677                                 else if (bin.Text.Length > 0)
678                                         Value = bin.Text;
679                                 else
680                                         Value = GetDefaultBoundText ();
681                         } else {
682                                 Text = Value = GetDefaultBoundText ();
683                         }
684                 }
685                 
686                 internal void SetDataItem (object item)
687                 {
688                         dataItem = item;
689                 }
690                 
691                 internal void SetDataPath (string path)
692                 {
693                         DataPath = path;
694                 }
695                 
696                 internal void SetDataBound (bool bound)
697                 {
698                         DataBound = bound;
699                 }
700                 
701                 string GetDefaultBoundText ()
702                 {
703                         if (hierarchyData != null) return hierarchyData.ToString ();
704                         else if (dataItem != null) return dataItem.ToString ();
705                         else return string.Empty;
706                 }
707                 
708                 string GetDataItemType ()
709                 {
710                         if (hierarchyData != null) return hierarchyData.Type;
711                         else if (dataItem != null) return dataItem.GetType().ToString ();
712                         else return string.Empty;
713                 }
714                                 
715                 internal bool IsParentNode {
716                         get { return ChildNodes.Count > 0 || (PopulateOnDemand && !Populated); }
717                 }
718                 
719                 internal bool IsLeafNode {
720                         get { return !IsParentNode; }
721                 }
722                 
723                 internal bool IsRootNode {
724                         get { return Depth == 0; }
725                 }
726                 
727                 TreeNodeBinding GetBinding ()
728                 {
729                         if (tree == null) return null;
730                         if (gotBinding) return binding;
731                         binding = tree.FindBindingForNode (GetDataItemType (), Depth);
732                         gotBinding = true;
733                         return binding;
734                 }
735                 
736                 object GetBoundPropertyValue (string name)
737                 {
738                         if (boundProperties == null) {
739                                 if (hierarchyData != null)
740                                         boundProperties = TypeDescriptor.GetProperties (hierarchyData);
741                                 else
742                                         boundProperties = TypeDescriptor.GetProperties (dataItem);
743                         }
744                         
745                         PropertyDescriptor prop = boundProperties.Find (name, true);
746                         if (prop == null)
747                                 throw new InvalidOperationException ("Property '" + name + "' not found in data bound item");
748                                 
749                         if (hierarchyData != null)
750                                 return prop.GetValue (hierarchyData);
751                         else
752                                 return prop.GetValue (dataItem);
753                 }
754
755                 internal void BeginRenderText (HtmlTextWriter writer)
756                 {
757                         RenderPreText (writer);
758                 }
759                 
760                 internal void EndRenderText (HtmlTextWriter writer)
761                 {
762                         RenderPostText (writer);
763                 }
764                 
765                 protected virtual void RenderPreText (HtmlTextWriter writer)
766                 {
767                 }
768                 
769                 protected virtual void RenderPostText (HtmlTextWriter writer)
770                 {
771                 }
772         }
773 }
774
775 #endif