PageRenderTime 232ms CodeModel.GetById 61ms app.highlight 74ms RepoModel.GetById 47ms app.codeStats 0ms

/IZWebFileManager/FolderTree.cs

http://izwebfilemanager.googlecode.com/
C# | 979 lines | 780 code | 158 blank | 41 comment | 165 complexity | df908acd3774ed93c8e01f77c446d0a4 MD5 | raw file
  1// Copyright (C) 2006 Igor Zelmanovich <izwebfilemanager@gmail.com>
  2//
  3// This program is free software; you can redistribute it and/or modify
  4// it under the terms of the GNU General Public License as published by
  5// the Free Software Foundation; either version 2 of the License, or
  6// (at your option) any later version.
  7//
  8// This program is distributed in the hope that it will be useful,
  9// but WITHOUT ANY WARRANTY; without even the implied warranty of
 10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11// GNU General Public License for more details.
 12//
 13// You should have received a copy of the GNU General Public License
 14// along with this program; if not, write to the Free Software
 15// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 16
 17using System;
 18using System.Collections.Generic;
 19using System.ComponentModel;
 20using System.Text;
 21using System.Web;
 22using System.Web.UI;
 23using System.Web.UI.WebControls;
 24using System.Globalization;
 25using IZ.WebFileManager.Components;
 26using System.IO;
 27using System.Drawing;
 28using System.Collections.ObjectModel;
 29using System.Collections.Specialized;
 30using System.Collections;
 31
 32namespace IZ.WebFileManager
 33{
 34	[SupportsEventValidation]
 35	[DefaultProperty ("Text")]
 36	[ToolboxData ("<{0}:FolderTree runat=\"server\"></{0}:FolderTree>")]
 37	public sealed class FolderTree : FileManagerControlBase, ICallbackEventHandler
 38	{
 39		#region Constructors
 40
 41		public FolderTree ()
 42			: base () {
 43			InitTreeView ();
 44		}
 45
 46		public FolderTree (FileManagerController controller)
 47			: base (controller) {
 48			InitTreeView ();
 49		}
 50
 51		public FolderTree (FileManagerController controller, FileView fileFiew)
 52			: this (controller) {
 53			_fileView = fileFiew;
 54		}
 55
 56		#endregion
 57
 58		#region Fields
 59		FileView _fileView;
 60
 61		bool stylesPrepared;
 62		Style hoverNodeStyle;
 63		TreeNodeStyle nodeStyle;
 64		TreeNodeStyle selectedNodeStyle;
 65		Collection<FolderNode> _nodes;
 66		Style controlLinkStyle;
 67		Style nodeLinkStyle;
 68		Style selectedNodeLinkStyle;
 69		Style hoverNodeLinkStyle;
 70		int _registeredStylesCounter = -1;
 71		Dictionary<Style, string> _registeredStyleClassNames;
 72		#endregion
 73
 74		#region Events
 75		public EventHandler SelectedFolderChanged;
 76		#endregion
 77
 78		#region Properties
 79
 80		// TODO
 81		string SelectedPath {
 82			get { return null; }
 83		}
 84
 85		[DefaultValue ("")]
 86		[Description ("The url of the image to show when a node can be collapsed.")]
 87		[UrlProperty]
 88		[Category ("Appearance")]
 89		string CollapseImageUrl {
 90			get {
 91				return (string) (ViewState ["CollapseImageUrl"] ?? String.Empty);
 92			}
 93			set {
 94				ViewState ["CollapseImageUrl"] = value;
 95			}
 96		}
 97
 98		[DefaultValue ("")]
 99		[UrlProperty]
100		[Description ("The url of the image to show when a node can be expanded.")]
101		[Category ("Appearance")]
102		string ExpandImageUrl {
103			get {
104				return (string) (ViewState ["ExpandImageUrl"] ?? String.Empty);
105			}
106			set {
107				ViewState ["ExpandImageUrl"] = value;
108			}
109		}
110
111		[PersistenceMode (PersistenceMode.InnerProperty)]
112		[NotifyParentProperty (true)]
113		[DefaultValue (null)]
114		[Category ("Styles")]
115		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
116		Style HoverNodeStyle {
117			get {
118				if (hoverNodeStyle == null) {
119					hoverNodeStyle = new Style ();
120					if (IsTrackingViewState)
121						((IStateManager) hoverNodeStyle).TrackViewState ();
122				}
123				return hoverNodeStyle;
124			}
125		}
126
127		[DefaultValue (20)]
128		int NodeIndent {
129			get {
130				return (int) (ViewState ["NodeIndent"] ?? 20);
131			}
132			set {
133				ViewState ["NodeIndent"] = value;
134			}
135		}
136
137		Collection<FolderNode> Nodes {
138			get {
139				if (_nodes == null) {
140					_nodes = new Collection<FolderNode> ();
141				}
142				return _nodes;
143			}
144		}
145
146		[PersistenceMode (PersistenceMode.InnerProperty)]
147		[NotifyParentProperty (true)]
148		[DefaultValue (null)]
149		[Category ("Styles")]
150		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
151		TreeNodeStyle NodeStyle {
152			get {
153				if (nodeStyle == null) {
154					nodeStyle = new TreeNodeStyle ();
155					if (IsTrackingViewState)
156						((IStateManager) nodeStyle).TrackViewState ();
157				}
158				return nodeStyle;
159			}
160		}
161
162		[UrlProperty]
163		[DefaultValue ("")]
164		[Description ("The url of the image to show for leaf nodes.")]
165		[Category ("Appearance")]
166		string NoExpandImageUrl {
167			get {
168				return (string) (ViewState ["NoExpandImageUrl"] ?? String.Empty);
169			}
170			set {
171				ViewState ["NoExpandImageUrl"] = value;
172			}
173		}
174
175		char PathSeparator {
176			get {
177				return '/';
178			}
179		}
180
181		string ClientScriptReference {
182			get {
183				if (_fileView != null)
184					return FileManagerController.ClientScriptObjectNamePrefix + _fileView.ClientID + "_FolderTree";
185				return FileManagerController.ClientScriptObjectNamePrefix + ClientID;
186			}
187		}
188
189		[PersistenceMode (PersistenceMode.InnerProperty)]
190		[NotifyParentProperty (true)]
191		[DefaultValue (null)]
192		[Category ("Styles")]
193		[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
194		TreeNodeStyle SelectedNodeStyle {
195			get {
196				if (selectedNodeStyle == null) {
197					selectedNodeStyle = new TreeNodeStyle ();
198					if (IsTrackingViewState)
199						((IStateManager) selectedNodeStyle).TrackViewState ();
200				}
201				return selectedNodeStyle;
202			}
203		}
204
205		Style ControlLinkStyle {
206			get {
207				if (controlLinkStyle == null) {
208					controlLinkStyle = new Style ();
209					controlLinkStyle.Font.Underline = false;
210					//controlLinkStyle.AlwaysRenderTextDecoration = true;
211				}
212				return controlLinkStyle;
213			}
214		}
215
216		Style NodeLinkStyle {
217			get {
218				if (nodeLinkStyle == null) {
219					nodeLinkStyle = new Style ();
220				}
221				return nodeLinkStyle;
222			}
223		}
224
225		Style SelectedNodeLinkStyle {
226			get {
227				if (selectedNodeLinkStyle == null) {
228					selectedNodeLinkStyle = new Style ();
229				}
230				return selectedNodeLinkStyle;
231			}
232		}
233
234		Style HoverNodeLinkStyle {
235			get {
236				if (hoverNodeLinkStyle == null) {
237					hoverNodeLinkStyle = new Style ();
238				}
239				return hoverNodeLinkStyle;
240			}
241		}
242
243		[DefaultValue (false)]
244		bool ShowLines {
245			get {
246				return (bool) (ViewState ["ShowLines"] ?? false);
247			}
248			set {
249				ViewState ["ShowLines"] = value;
250			}
251		}
252
253		protected override HtmlTextWriterTag TagKey {
254			get { return HtmlTextWriterTag.Div; }
255		}
256
257		#endregion
258
259		#region  Methods
260
261		protected override void AddAttributesToRender (HtmlTextWriter writer) {
262			base.AddAttributesToRender (writer);
263
264			writer.AddStyleAttribute (HtmlTextWriterStyle.Position, "relative");
265			writer.AddStyleAttribute (HtmlTextWriterStyle.ZIndex, "20");
266			writer.AddStyleAttribute (HtmlTextWriterStyle.Overflow, "auto");
267		}
268
269		void InitTreeView () {
270			NodeStyle.HorizontalPadding = 4;
271			NodeStyle.Height = 20;
272			NodeStyle.ForeColor = Color.Black;
273			NodeStyle.Font.Names = new string [] { "Tahoma", "Verdana", "Geneva", "Arial", "Helvetica", "sans-serif" };
274			NodeStyle.Font.Size = FontUnit.Parse ("11px", null);
275			HoverNodeStyle.Font.Underline = true;
276			SelectedNodeStyle.Font.Underline = false;
277			SelectedNodeStyle.ForeColor = Color.White;
278			SelectedNodeStyle.BackColor = Color.FromArgb (0x316AC5);
279		}
280
281		void OnSelectedFolderChanged (object sender, EventArgs e) {
282			if (SelectedFolderChanged != null)
283				SelectedFolderChanged (this, EventArgs.Empty);
284		}
285
286		void OnFolderPopulate (FolderNode node) {
287			Controller.EnsureDefaults ();
288
289			DirectoryInfo directoryInfo = Controller.ResolveFileManagerItemInfo (node.ValuePath).Directory;
290			if (!directoryInfo.Exists)
291				return;
292
293			DirectoryProvider provider = new DirectoryProvider (directoryInfo, SortMode.Name, SortDirection.Ascending);
294
295			foreach (DirectoryInfo dir in provider.GetFileSystemInfos (DirectoryProvider.FileSystemInfosFilter.Directories)) {
296
297				FileViewItem item = new FileViewItem (dir, this);
298				if (!ShowHiddenFilesAndFolders && item.Hidden)
299					continue;
300				
301				FolderNode treeNode = new FolderNode (this);
302				node.ChildNodes.Add (treeNode);
303				treeNode.Text = dir.Name;
304				treeNode.Hidden = item.Hidden;
305				treeNode.ImageUrl = Controller.GetFolderSmallImage (dir);
306				if (_fileView != null)
307					treeNode.NavigateUrl = "javascript:WFM_" + _fileView.ClientID + ".Navigate(\"" + treeNode.ValuePath + "\");";
308			}
309		}
310
311		void BuildTree () {
312
313			for (int i = 0; i < RootDirectories.Count; i++) {
314				RootDirectory rootDir = RootDirectories [i];
315				FolderNode treeNode = new FolderNode (this);
316				Nodes.Add (treeNode);
317				treeNode.ImageUrl = Controller.GetRootFolderSmallImage (rootDir);
318				SetText (rootDir, treeNode, i);
319				treeNode.Value = PathSeparator + rootDir.TextInternal;
320				if (_fileView != null)
321					treeNode.NavigateUrl = "javascript:WFM_" + _fileView.ClientID + ".Navigate(\"" + treeNode.ValuePath + "\");";
322			}
323
324			for (int i = 0; i < RootDirectories.Count; i++)
325				ExpandRecursive (Nodes [i], RootDirectories [i].ExpandDepth);
326
327		}
328
329		void SetText (RootDirectory rootDir, FolderNode treeNode, int index) {
330			if (rootDir.ShowRootIndex)
331				treeNode.Text = String.Format (CultureInfo.InvariantCulture, "{0} ([{1}])", rootDir.TextInternal, index);
332			else
333				treeNode.Text = rootDir.TextInternal;
334		}
335
336		void ExpandRecursive (FolderNode treeNode, int dept) {
337			if (dept == 0)
338				return;
339
340			treeNode.Expand ();
341			foreach (FolderNode child in treeNode.ChildNodes)
342				ExpandRecursive (child, dept - 1);
343		}
344
345		#endregion
346
347		protected override void TrackViewState () {
348
349			base.TrackViewState ();
350			if (hoverNodeStyle != null) {
351				((IStateManager) hoverNodeStyle).TrackViewState ();
352			}
353			if (nodeStyle != null) {
354				((IStateManager) nodeStyle).TrackViewState ();
355			}
356			if (selectedNodeStyle != null) {
357				((IStateManager) selectedNodeStyle).TrackViewState ();
358			}
359		}
360
361		protected override object SaveViewState () {
362			object [] states = new object [4];
363			states [0] = base.SaveViewState ();
364			states [1] = (hoverNodeStyle == null ? null : ((IStateManager) hoverNodeStyle).SaveViewState ());
365			states [2] = (nodeStyle == null ? null : ((IStateManager) nodeStyle).SaveViewState ());
366			states [3] = (selectedNodeStyle == null ? null : ((IStateManager) selectedNodeStyle).SaveViewState ());
367
368			for (int i = states.Length - 1; i >= 0; i--) {
369				if (states [i] != null)
370					return states;
371			}
372
373			return null;
374		}
375
376		protected override void LoadViewState (object savedState) {
377			if (savedState == null)
378				return;
379
380			object [] states = (object []) savedState;
381			base.LoadViewState (states [0]);
382
383			if (states [1] != null)
384				((IStateManager) HoverNodeStyle).LoadViewState (states [1]);
385			if (states [2] != null)
386				((IStateManager) NodeStyle).LoadViewState (states [2]);
387			if (states [3] != null)
388				((IStateManager) SelectedNodeStyle).LoadViewState (states [3]);
389		}
390
391		string callbackResult;
392
393		void ICallbackEventHandler.RaiseCallbackEvent (string eventArgs) {
394			int index = eventArgs.IndexOf ('|');
395			FolderNode node = new FolderNode (this);
396			node.ValuePath = eventArgs.Substring (index + 1);
397
398			node.Expand ();
399			int num = node.ChildNodes.Count;
400			if (num == 0) {
401				callbackResult = "*";
402				return;
403			}
404
405			ArrayList levelLines = new ArrayList ();
406			for (int i = 0; i < index; i++)
407				levelLines.Add (eventArgs [i] == '1' ? this : null);
408
409			StringBuilder sb = new StringBuilder ();
410			HtmlTextWriter writer = new HtmlTextWriter (new StringWriter (sb));
411			EnsureStylesPrepared ();
412
413			for (int n = 0; n < num; n++)
414				RenderNode (writer, node.ChildNodes [n], index, levelLines, true, n < num - 1);
415
416			callbackResult = sb.ToString ();
417		}
418
419		string ICallbackEventHandler.GetCallbackResult () {
420			return callbackResult;
421		}
422
423		protected override ControlCollection CreateControlCollection () {
424			return new EmptyControlCollection (this);
425		}
426
427		protected override void OnPreRender (EventArgs e) {
428			base.OnPreRender (e);
429
430			if (Page.Header == null)
431				throw new InvalidOperationException ("Using FolderTree requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
432
433			Page.ClientScript.RegisterClientScriptResource (typeof (FolderTree), "IZ.WebFileManager.resources.FolderTree.js");
434			string callbackScript = string.Format ("FolderTree.prototype.PopulateNode = function(argument, clientCallback, context, clientErrorCallback) {{ {0}; }};\n", Page.ClientScript.GetCallbackEventReference ("this._uniqueId", "argument", "clientCallback", "context", "clientErrorCallback", true));
435			Page.ClientScript.RegisterClientScriptBlock (typeof (FolderTree), "FolderTree.prototype.PopulateNode", callbackScript, true);
436
437			string folderTree = ClientScriptReference;
438			StringBuilder script = new StringBuilder ();
439			script.AppendLine ("var " + folderTree + "= new FolderTree ('" + Controller.ClientID + "','" + ClientID + "','" + UniqueID + "','" + GetNodeImageUrl ("Expand") + "','" + GetNodeImageUrl ("Collapse") + "','" + GetNodeImageUrl ("NoExpand") + "');");
440
441			EnsureStylesPrepared ();
442
443			if (selectedNodeStyle != null) {
444				script.AppendFormat ("{0}._selectedClass = '{1}';\n", folderTree, _registeredStyleClassNames [SelectedNodeStyle]);
445				script.AppendFormat ("{0}._selectedLinkClass = '{1}';\n", folderTree, _registeredStyleClassNames [SelectedNodeLinkStyle]);
446			}
447
448			if (hoverNodeStyle != null) {
449				script.AppendFormat ("{0}._hoverClass = '{1}';\n", folderTree, _registeredStyleClassNames [HoverNodeStyle]);
450				script.AppendFormat ("{0}._hoverLinkClass = '{1}';\n", folderTree, _registeredStyleClassNames [HoverNodeLinkStyle]);
451			}
452			if (_fileView != null)
453				script.AppendLine (folderTree + ".Navigate (['" + String.Join ("','", Controller.GetPathHashCodes (_fileView.CurrentDirectory.FileManagerPath)) + "'],0);");
454
455			Page.ClientScript.RegisterStartupScript (typeof (FolderTree), this.ClientID, script.ToString (), true);
456
457			BuildTree ();
458		}
459
460		void EnsureStylesPrepared () {
461			if (stylesPrepared)
462				return;
463			stylesPrepared = true;
464			PrepareStyles ();
465		}
466
467		private void PrepareStyles () {
468			_registeredStyleClassNames = new Dictionary<Style, string> ();
469
470			ControlLinkStyle.Font.CopyFrom (ControlStyle.Font);
471			ControlLinkStyle.ForeColor = ControlStyle.ForeColor;
472			RegisterStyle (ControlLinkStyle);
473
474			if (nodeStyle != null)
475				RegisterStyle (NodeStyle, NodeLinkStyle);
476
477			if (selectedNodeStyle != null)
478				RegisterStyle (SelectedNodeStyle, SelectedNodeLinkStyle);
479
480			if (hoverNodeStyle != null)
481				RegisterStyle (HoverNodeStyle, HoverNodeLinkStyle);
482		}
483
484		void RegisterStyle (Style baseStyle, Style linkStyle) {
485			linkStyle.Font.CopyFrom (baseStyle.Font);
486			linkStyle.ForeColor = baseStyle.ForeColor;
487			linkStyle.BorderStyle = BorderStyle.None;
488			baseStyle.Font.ClearDefaults ();
489			RegisterStyle (linkStyle);
490			RegisterStyle (baseStyle);
491		}
492
493		string IncrementStyleClassName () {
494			_registeredStylesCounter++;
495			return ClientID + "_" + _registeredStylesCounter;
496		}
497
498		void RegisterStyle (Style baseStyle) {
499			string className = IncrementStyleClassName ();
500			Page.Header.StyleSheet.CreateStyleRule (baseStyle, this, "." + className);
501			_registeredStyleClassNames [baseStyle] = className;
502		}
503
504		string GetBindingKey (string dataMember, int depth) {
505			return dataMember + " " + depth;
506		}
507
508		protected override void RenderContents (HtmlTextWriter writer) {
509			int num = Nodes.Count;
510			for (int n = 0; n < num; n++)
511				RenderNode (writer, Nodes [n], 0, new ArrayList (), n > 0, n < num - 1);
512		}
513
514		void RenderNode (HtmlTextWriter writer, FolderNode node, int level, ArrayList levelLines, bool hasPrevious, bool hasNext) {
515			string nodeImage;
516
517			bool hasChildNodes = node.Expanded ? node.ChildNodes.Count > 0 : true;
518			if (hasNext)
519				levelLines.Add (this);
520			else
521				levelLines.Add (null);
522
523			writer.AddAttribute ("cellpadding", "0", false);
524			writer.AddAttribute ("cellspacing", "0", false);
525			writer.AddStyleAttribute ("border-width", "0");
526			writer.RenderBeginTag (HtmlTextWriterTag.Table);
527
528			Unit nodeSpacing = GetNodeSpacing (node);
529
530			if (nodeSpacing != Unit.Empty && (node.Depth > 0 || node.Index > 0))
531				RenderMenuItemSpacing (writer, nodeSpacing);
532
533			writer.RenderBeginTag (HtmlTextWriterTag.Tr);
534
535			// Vertical lines from previous levels
536
537			nodeImage = GetNodeImageUrl ("I");
538			for (int n = 0; n < level; n++) {
539				writer.RenderBeginTag (HtmlTextWriterTag.Td);
540				writer.AddStyleAttribute ("width", NodeIndent + "px");
541				//writer.AddStyleAttribute ("height", "1px");
542				writer.RenderBeginTag (HtmlTextWriterTag.Div);
543				if (ShowLines && levelLines [n] != null) {
544					writer.AddAttribute ("src", nodeImage);
545					writer.AddAttribute (HtmlTextWriterAttribute.Alt, "", false);
546					writer.RenderBeginTag (HtmlTextWriterTag.Img);
547					writer.RenderEndTag ();
548				}
549				writer.RenderEndTag ();
550				writer.RenderEndTag ();	// TD
551			}
552
553			// Node image + line
554
555			string shape = String.Empty;
556
557			if (ShowLines) {
558				if (hasPrevious && hasNext)
559					shape = "T";
560				else if (hasPrevious && !hasNext)
561					shape = "L";
562				else if (!hasPrevious && hasNext)
563					shape = "R";
564				else
565					shape = "Dash";
566			}
567
568			if (hasChildNodes) {
569				if (node.Expanded)
570					shape += "Collapse";
571				else
572					shape += "Expand";
573			}
574			else if (!ShowLines)
575				shape = "NoExpand";
576
577			if (!String.IsNullOrEmpty (shape)) {
578				nodeImage = GetNodeImageUrl (shape);
579				writer.RenderBeginTag (HtmlTextWriterTag.Td);	// TD
580
581				writer.AddAttribute (HtmlTextWriterAttribute.Href, GetClientExpandEvent (node));
582				writer.RenderBeginTag (HtmlTextWriterTag.A);	// Anchor
583
584				writer.AddAttribute ("alt", String.Empty);
585
586				writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, "img"));
587				writer.AddAttribute ("src", nodeImage);
588				writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0");
589				writer.RenderBeginTag (HtmlTextWriterTag.Img);
590				writer.RenderEndTag ();
591
592				writer.RenderEndTag ();		// Anchor
593
594				writer.RenderEndTag ();		// TD
595			}
596
597			writer.RenderBeginTag (HtmlTextWriterTag.Td);
598			writer.AddAttribute ("onmouseout", ClientScriptReference + ".UnhoverNode(this)", false);
599			writer.AddAttribute ("onmouseover", ClientScriptReference + ".HoverNode(this, event)", false);
600			writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, null));
601			writer.RenderBeginTag (HtmlTextWriterTag.Div);
602			writer.AddAttribute ("cellpadding", "0", false);
603			writer.AddAttribute ("cellspacing", "0", false);
604			writer.AddStyleAttribute ("border-width", "0");
605			writer.RenderBeginTag (HtmlTextWriterTag.Table);
606			writer.RenderBeginTag (HtmlTextWriterTag.Tr);
607
608			// Node icon
609
610			string imageUrl = node.ImageUrl.Length > 0 ? ResolveClientUrl (node.ImageUrl) : null;
611
612			if (!String.IsNullOrEmpty (imageUrl)) {
613				if (node.Hidden)
614					Controller.HiddenItemStyle.AddAttributesToRender (writer);
615				writer.RenderBeginTag (HtmlTextWriterTag.Td);	// TD
616				BeginNodeTag (writer, node);
617				writer.AddAttribute ("src", imageUrl);
618				writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0");
619				writer.AddAttribute ("alt", String.Empty);
620				writer.RenderBeginTag (HtmlTextWriterTag.Img);
621				writer.RenderEndTag ();	// IMG
622				writer.RenderEndTag ();	// node tag
623				writer.RenderEndTag ();	// TD
624			}
625
626			writer.AddStyleAttribute ("white-space", "nowrap");
627			AddNodeStyle (writer, node, level);
628
629			writer.AddAttribute ("nodepath", GetNodePath (node, levelLines));
630			writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, "node"));
631
632			writer.RenderBeginTag (HtmlTextWriterTag.Td);	// TD
633
634			AddNodeLinkStyle (writer, node, level);
635			BeginNodeTag (writer, node);
636			writer.Write (node.Text);
637			writer.RenderEndTag ();	// node tag
638
639			writer.RenderEndTag ();	// TD
640
641			writer.RenderEndTag (); // TR
642			writer.RenderEndTag (); // TABLE
643			writer.RenderEndTag (); // DIV
644			writer.RenderEndTag (); // TD
645
646			writer.RenderEndTag ();	// TR
647
648			if (nodeSpacing != Unit.Empty)
649				RenderMenuItemSpacing (writer, nodeSpacing);
650
651			writer.RenderEndTag ();	// TABLE
652
653			// Children
654
655			if (!node.Expanded || node.ChildNodes.Count == 0)
656				writer.AddStyleAttribute ("display", "none");
657			else
658				writer.AddStyleAttribute ("display", "block");
659			writer.AddAttribute (HtmlTextWriterAttribute.Id, GetNodeClientId (node, "span"));
660			writer.RenderBeginTag (HtmlTextWriterTag.Span);
661
662			if (node.Expanded) {
663				//AddChildrenPadding (writer, node);
664				int num = node.ChildNodes.Count;
665				if (num == 0)
666					writer.Write ("*");
667				else
668					for (int n = 0; n < num; n++)
669						RenderNode (writer, node.ChildNodes [n], level + 1, levelLines, true, n < num - 1);
670				//if (hasNext)
671				//    AddChildrenPadding (writer, node);
672			}
673			writer.RenderEndTag ();	// SPAN
674
675			levelLines.RemoveAt (level);
676		}
677
678		//private void AddChildrenPadding (HtmlTextWriter writer, FolderNode node) {
679		//    int level = node.Depth;
680		//    Unit cnp = Unit.Empty;
681
682		//    if (cnp.IsEmpty && nodeStyle != null)
683		//        cnp = nodeStyle.ChildNodesPadding;
684
685		//    double value;
686		//    if (cnp.IsEmpty || (value = cnp.Value) == 0 || cnp.Type != UnitType.Pixel)
687		//        return;
688
689		//    writer.RenderBeginTag (HtmlTextWriterTag.Table);
690		//    writer.AddAttribute ("height", ((int) value).ToString (), false);
691		//    writer.RenderBeginTag (HtmlTextWriterTag.Tr);
692		//    writer.RenderBeginTag (HtmlTextWriterTag.Td);
693		//    writer.RenderEndTag (); // td
694		//    writer.RenderEndTag (); // tr
695		//    writer.RenderEndTag (); // table
696		//}
697
698		private void RenderMenuItemSpacing (HtmlTextWriter writer, Unit itemSpacing) {
699			writer.AddStyleAttribute ("height", itemSpacing.ToString ());
700			writer.RenderBeginTag (HtmlTextWriterTag.Tr);
701			writer.RenderBeginTag (HtmlTextWriterTag.Td);
702			writer.RenderEndTag ();
703			writer.RenderEndTag ();
704		}
705
706		private Unit GetNodeSpacing (FolderNode node) {
707			if (node.Selected && selectedNodeStyle != null && selectedNodeStyle.NodeSpacing != Unit.Empty)
708				return selectedNodeStyle.NodeSpacing;
709			if (nodeStyle != null)
710				return nodeStyle.NodeSpacing;
711			else
712				return Unit.Empty;
713		}
714
715		void AddNodeStyle (HtmlTextWriter writer, FolderNode node, int level) {
716			StringBuilder sb = new StringBuilder ();
717			if (nodeStyle != null) {
718				sb.Append (" ");
719				sb.Append (nodeStyle.CssClass);
720				sb.Append (" ");
721				sb.Append (_registeredStyleClassNames [nodeStyle]);
722			}
723			if (node.ValuePath == SelectedPath && selectedNodeStyle != null) {
724				sb.Append (" ");
725				sb.Append (selectedNodeStyle.CssClass);
726				sb.Append (" ");
727				sb.Append (_registeredStyleClassNames [selectedNodeStyle]);
728			}
729			writer.AddAttribute (HtmlTextWriterAttribute.Class, sb.ToString ());
730		}
731
732		void AddNodeLinkStyle (HtmlTextWriter writer, FolderNode node, int level) {
733			StringBuilder sb = new StringBuilder ();
734			sb.Append (" ");
735			sb.Append (_registeredStyleClassNames [ControlLinkStyle]);
736
737			if (nodeStyle != null) {
738				sb.Append (" ");
739				sb.Append (nodeLinkStyle.CssClass);
740				sb.Append (" ");
741				sb.Append (_registeredStyleClassNames [nodeLinkStyle]);
742			}
743			if (node.ValuePath == SelectedPath && selectedNodeStyle != null) {
744				sb.Append (" ");
745				sb.Append (selectedNodeLinkStyle.CssClass);
746				sb.Append (" ");
747				sb.Append (_registeredStyleClassNames [selectedNodeLinkStyle]);
748			}
749			writer.AddAttribute (HtmlTextWriterAttribute.Class, sb.ToString ());
750		}
751
752		void BeginNodeTag (HtmlTextWriter writer, FolderNode node) {
753			if (node.ToolTip.Length > 0)
754				writer.AddAttribute ("title", node.ToolTip);
755
756			if (!String.IsNullOrEmpty (node.NavigateUrl)) {
757				string navUrl = ResolveClientUrl (node.NavigateUrl);
758				writer.AddAttribute ("href", navUrl);
759				writer.RenderBeginTag (HtmlTextWriterTag.A);
760			}
761			else
762				writer.RenderBeginTag (HtmlTextWriterTag.Span);
763		}
764
765		string GetNodeClientId (FolderNode node, string sufix) {
766			return ClientID + "_" + node.ClientID + (sufix != null ? "_" + sufix : "");
767		}
768
769		string GetNodeImageUrl (string shape) {
770			if (!ShowLines) {
771				if (shape == "Expand") {
772					if (!String.IsNullOrEmpty (ExpandImageUrl))
773						return ResolveClientUrl (ExpandImageUrl);
774				}
775				else if (shape == "Collapse") {
776					if (!String.IsNullOrEmpty (CollapseImageUrl))
777						return ResolveClientUrl (CollapseImageUrl);
778				}
779				else if (shape == "NoExpand") {
780					if (!String.IsNullOrEmpty (NoExpandImageUrl))
781						return ResolveClientUrl (NoExpandImageUrl);
782				}
783			}
784			return Page.ClientScript.GetWebResourceUrl (typeof (Page), "TreeView_Default_" + shape + ".gif");
785		}
786
787		string GetNodeIconUrl (string icon) {
788			return Page.ClientScript.GetWebResourceUrl (typeof (TreeView), icon + ".gif");
789		}
790
791		string GetClientExpandEvent (FolderNode node) {
792			return "javascript:" + ClientScriptReference + ".ToggleExpand (\"" + node.ClientID + "\")";
793		}
794
795		string GetNodePath (FolderNode node, ArrayList levelLines) {
796			StringBuilder arg = new StringBuilder ();
797			for (int i = 0; i < levelLines.Count; i++)
798				if (levelLines [i] == null)
799					arg.Append ('0');
800				else
801					arg.Append ('1');
802			arg.Append ('|');
803			arg.Append (node.ValuePath);
804			return arg.ToString ();
805		}
806
807		sealed class FolderNode
808		{
809			Collection<FolderNode> _nodes;
810			readonly FolderTree _owner;
811			FolderNode _parent;
812			int _index = -1;
813			int _depth = -1;
814			string _clientId;
815			string _valuePath;
816			string _imageUrl;
817			string _navigateUrl;
818			string _value;
819			string _text;
820			string _tooltip;
821			bool _expanded;
822			bool _selected;
823			bool _hidden;
824
825			public FolderNode (FolderTree owner) {
826				_owner = owner;
827			}
828
829			[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
830			[Browsable (false)]
831			public int Depth {
832				get {
833					if (_depth != -1)
834						return _depth;
835					_depth = 0;
836					FolderNode node = _parent;
837					while (node != null) {
838						_depth++;
839						node = node.Parent;
840					}
841					return _depth;
842				}
843			}
844
845			public Collection<FolderNode> ChildNodes {
846				get {
847					if (_nodes == null) {
848						_nodes = new FolderNodeCollection (this);
849					}
850					return _nodes;
851				}
852			}
853
854			public bool Expanded {
855				get { return _expanded; }
856			}
857			
858			public bool Hidden {
859				get { return _hidden; }
860				set { _hidden = value; }
861			}
862
863			public string ImageUrl {
864				get { return _imageUrl ?? String.Empty; }
865				set { _imageUrl = value; }
866			}
867
868			public string NavigateUrl {
869				get { return _navigateUrl ?? String.Empty; }
870				set { _navigateUrl = value; }
871			}
872
873			public string Text {
874				get { return _text ?? (_value ?? String.Empty); }
875				set { _text = value; }
876			}
877
878			public string ToolTip {
879				get { return _tooltip ?? String.Empty; }
880				set { _tooltip = value; }
881			}
882
883			public string Value {
884				get { return _value ?? (_text ?? String.Empty); }
885				set { _value = value; }
886			}
887
888			[DefaultValue (false)]
889			public bool Selected {
890				get { return _selected; }
891				set { _selected = value; }
892			}
893
894			[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
895			[Browsable (false)]
896			public FolderNode Parent {
897				get { return _parent; }
898			}
899
900			[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
901			[Browsable (false)]
902			public string ValuePath {
903				get {
904					if (_owner == null)
905						return Value;
906					if (_valuePath == null) {
907						StringBuilder sb = new StringBuilder (Value);
908						FolderNode node = Parent;
909						if (node != null) {
910							sb.Insert (0, _owner.PathSeparator);
911							sb.Insert (0, node.ValuePath);
912							node = node.Parent;
913						}
914						_valuePath = sb.ToString ();
915					}
916					return _valuePath;
917				}
918				set {
919					_valuePath = value;
920				}
921			}
922
923			internal int Index {
924				get {
925					if (_index < 0) {
926						_index = 0;
927						if (Parent != null)
928							_index = Parent.ChildNodes.IndexOf (this);
929					}
930					return _index;
931				}
932			}
933
934			internal void SetParent (FolderNode node) {
935				_parent = node;
936				_index = -1;
937				_depth = -1;
938				_clientId = null;
939				_valuePath = null;
940			}
941
942			internal string ClientID {
943				get {
944					if (_clientId == null) {
945						_clientId = _owner.Controller.GetPathHashCode (ValuePath);
946					}
947					return _clientId;
948				}
949			}
950
951			public void Expand () {
952				if (Expanded)
953					return;
954				_expanded = true;
955				_owner.OnFolderPopulate (this);
956			}
957		}
958
959		class FolderNodeCollection : Collection<FolderNode>
960		{
961			readonly FolderNode _owner;
962
963			public FolderNodeCollection (FolderNode owner) {
964				_owner = owner;
965			}
966
967			protected override void InsertItem (int index, FolderNode item) {
968				base.InsertItem (index, item);
969				item.SetParent (_owner);
970			}
971
972			protected override void SetItem (int index, FolderNode item) {
973				base.SetItem (index, item);
974				item.SetParent (_owner);
975			}
976		}
977
978	}
979}