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