Browse Source

[asp.net] Menu 4.0 List mode rendering

Marek Habersack 15 years ago
parent
commit
f1d69fd4bc

+ 506 - 0
mcs/class/System.Web/System.Web.UI.WebControls/BaseMenuRenderer.cs

@@ -0,0 +1,506 @@
+//
+// Authors:
+//	Marek Habersack <[email protected]>
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.HtmlControls;
+
+namespace System.Web.UI.WebControls
+{
+	abstract class BaseMenuRenderer : IMenuRenderer
+	{
+		protected sealed class OwnerContext
+		{
+			BaseMenuRenderer container;
+			
+			string staticPopOutImageTextFormatString;
+			string dynamicPopOutImageTextFormatString;
+			string dynamicTopSeparatorImageUrl;
+			string dynamicBottomSeparatorImageUrl;
+			string staticTopSeparatorImageUrl;
+			string staticBottomSeparatorImageUrl;
+			List <Style> levelMenuItemLinkStyles;
+			List<Style> levelSelectedLinkStyles;
+			Style staticMenuItemLinkStyle;
+			Style dynamicMenuItemLinkStyle;
+			MenuItemStyle staticSelectedStyle;
+			Style staticSelectedLinkStyle;
+			MenuItemStyle dynamicSelectedStyle;
+			Style dynamicSelectedLinkStyle;
+			MenuItemStyleCollection levelSelectedStyles;
+			ITemplate dynamicItemTemplate;
+			bool dynamicItemTemplateQueried;
+			
+			public readonly MenuItemStyle StaticMenuItemStyle;
+			public readonly MenuItemStyle DynamicMenuItemStyle;
+			public readonly MenuItemStyleCollection LevelMenuItemStyles;
+			public readonly Style ControlLinkStyle;
+			public readonly HtmlHead Header;
+			public readonly string ClientID;
+			public readonly int StaticDisplayLevels;
+			public readonly bool IsVertical;
+			public readonly MenuItem SelectedItem;
+			public readonly Unit StaticSubMenuIndent;
+			
+			public string StaticPopOutImageTextFormatString {
+				get {
+					if (staticPopOutImageTextFormatString == null)
+						staticPopOutImageTextFormatString = container.Owner.StaticPopOutImageTextFormatString;
+
+					return staticPopOutImageTextFormatString;
+				}
+			}
+
+			public string DynamicPopOutImageTextFormatString {
+				get {
+					if (dynamicPopOutImageTextFormatString == null)
+						dynamicPopOutImageTextFormatString = container.Owner.DynamicPopOutImageTextFormatString;
+
+					return dynamicPopOutImageTextFormatString;
+				}
+			}
+
+			public string DynamicTopSeparatorImageUrl {
+				get {
+					if (dynamicTopSeparatorImageUrl == null)
+						dynamicTopSeparatorImageUrl = container.Owner.DynamicTopSeparatorImageUrl;
+
+					return dynamicTopSeparatorImageUrl;
+				}
+			}
+			
+			public string DynamicBottomSeparatorImageUrl {
+				get {
+					if (dynamicBottomSeparatorImageUrl == null)
+						dynamicBottomSeparatorImageUrl = container.Owner.DynamicBottomSeparatorImageUrl;
+
+					return dynamicBottomSeparatorImageUrl;
+				}
+			}
+
+			public string StaticTopSeparatorImageUrl {
+				get {
+					if (staticTopSeparatorImageUrl == null)
+						staticTopSeparatorImageUrl = container.Owner.StaticTopSeparatorImageUrl;
+
+					return staticBottomSeparatorImageUrl;
+				}
+			}
+			
+			public string StaticBottomSeparatorImageUrl {
+				get {
+					if (staticBottomSeparatorImageUrl == null)
+						staticBottomSeparatorImageUrl = container.Owner.StaticBottomSeparatorImageUrl;
+
+					return staticBottomSeparatorImageUrl;
+				}
+			}
+			
+			public List <Style> LevelMenuItemLinkStyles {
+				get {
+					if (levelMenuItemLinkStyles == null)
+						levelMenuItemLinkStyles = container.Owner.LevelMenuItemLinkStyles;
+
+					return levelMenuItemLinkStyles;
+				}
+			}
+
+			public List<Style> LevelSelectedLinkStyles {
+				get {
+					if (levelSelectedLinkStyles == null)
+						levelSelectedLinkStyles = container.Owner.LevelSelectedLinkStyles;
+
+					return levelSelectedLinkStyles;
+				}
+			}
+			
+		
+			public Style StaticMenuItemLinkStyle {
+				get {
+					if (staticMenuItemLinkStyle == null)
+						staticMenuItemLinkStyle = container.Owner.StaticMenuItemLinkStyle;
+
+					return staticMenuItemLinkStyle;
+				}
+			}
+
+			public Style DynamicMenuItemLinkStyle {
+				get {
+					if (dynamicMenuItemLinkStyle == null)
+						dynamicMenuItemLinkStyle = container.Owner.DynamicMenuItemLinkStyle;
+
+					return dynamicMenuItemLinkStyle;
+				}
+			}
+
+			public MenuItemStyle StaticSelectedStyle {
+				get {
+					if (staticSelectedStyle == null)
+						staticSelectedStyle = container.Owner.StaticSelectedStyle;
+
+					return staticSelectedStyle;
+				}
+			}
+		
+			public MenuItemStyle DynamicSelectedStyle {
+				get {
+					if (dynamicSelectedStyle == null)
+						dynamicSelectedStyle = container.Owner.DynamicSelectedStyle;
+
+					return dynamicSelectedStyle;
+				}
+			}
+
+			public Style StaticSelectedLinkStyle {
+				get {
+					if (staticSelectedLinkStyle == null)
+						staticSelectedLinkStyle = container.Owner.StaticSelectedLinkStyle;
+
+					return staticSelectedLinkStyle;
+				}
+			}
+		
+			public Style DynamicSelectedLinkStyle {
+				get {
+					if (dynamicSelectedLinkStyle == null)
+						dynamicSelectedLinkStyle = container.Owner.DynamicSelectedLinkStyle;
+
+					return dynamicSelectedLinkStyle;
+				}
+			}
+
+			public MenuItemStyleCollection LevelSelectedStyles {
+				get {
+					if (levelSelectedStyles == null)
+						levelSelectedStyles = container.Owner.LevelSelectedStyles;
+
+					return levelSelectedStyles;
+				}
+			}
+			
+			public ITemplate DynamicItemTemplate {
+				get {
+					if (!dynamicItemTemplateQueried && dynamicItemTemplate == null) {
+						dynamicItemTemplate = container.Owner.DynamicItemTemplate;
+						dynamicItemTemplateQueried = true;
+					}
+
+					return dynamicItemTemplate;
+				}
+			}
+		
+			public OwnerContext (BaseMenuRenderer container)
+			{
+				if (container == null)
+					throw new ArgumentNullException ("container");
+
+				this.container = container;
+				Menu owner = container.Owner;
+				Page page = owner.Page;
+
+				Header = page != null ? page.Header : null;
+				ClientID = owner.ClientID;
+				IsVertical = owner.Orientation == Orientation.Vertical;
+				StaticSubMenuIndent = owner.StaticSubMenuIndent;
+				SelectedItem = owner.SelectedItem;
+				ControlLinkStyle = owner.ControlLinkStyle;
+				StaticDisplayLevels = owner.StaticDisplayLevels;
+				StaticMenuItemStyle = owner.StaticMenuItemStyleInternal;
+				DynamicMenuItemStyle = owner.DynamicMenuItemStyleInternal;
+				LevelMenuItemStyles = owner.LevelMenuItemStyles;
+			}
+		}
+
+		int registeredStylesCounter = -1;
+		
+		public abstract HtmlTextWriterTag Tag { get; }
+		
+		protected Menu Owner {
+			get;
+			private set;
+		}
+		
+		public BaseMenuRenderer (Menu owner)
+		{
+			if (owner == null)
+				throw new ArgumentNullException ("owner");
+
+			this.Owner = owner;
+		}
+		
+		public virtual void AddAttributesToRender (HtmlTextWriter writer)
+		{
+			Menu owner = Owner;
+			Page page = owner.Page;
+			SubMenuStyle staticMenuStyle = owner.StaticMenuStyleInternal;
+			SubMenuStyleCollection levelSubMenuStyles = owner.LevelSubMenuStylesInternal;
+			bool haveSubStyles = levelSubMenuStyles != null && levelSubMenuStyles.Count > 0;
+			Style controlStyle = haveSubStyles || staticMenuStyle != null ? owner.ControlStyle : null;
+			
+			if (page != null && page.Header != null) {	
+				// styles are registered
+				if (staticMenuStyle != null) {
+					AddCssClass (controlStyle, staticMenuStyle.CssClass);
+					AddCssClass (controlStyle, staticMenuStyle.RegisteredCssClass);
+				}
+				if (haveSubStyles) {
+					AddCssClass (controlStyle, levelSubMenuStyles [0].CssClass);
+					AddCssClass (controlStyle, levelSubMenuStyles [0].RegisteredCssClass);
+				}
+			} else {
+				// styles are not registered
+				if (staticMenuStyle != null)
+					controlStyle.CopyFrom (staticMenuStyle);
+				if (haveSubStyles)
+					controlStyle.CopyFrom (levelSubMenuStyles [0]);
+			}
+		}
+		
+		public abstract void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script);
+		public abstract void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel);
+		public abstract void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast);
+		public abstract void RenderBeginTag (HtmlTextWriter writer, string skipLinkText);
+		public abstract void RenderEndTag (HtmlTextWriter writer);
+		public abstract void RenderContents (HtmlTextWriter writer);
+		public abstract bool IsDynamicItem (Menu owner, MenuItem item);
+		
+		protected abstract void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc);
+		
+		public virtual void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool notLast, bool isFirst)
+		{
+			var oc = new OwnerContext (this);
+			RenderMenuItem (writer, item, oc.IsVertical, notLast, isFirst, oc);
+		}
+		
+		public virtual void RenderMenuEndTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
+		{
+			writer.RenderEndTag ();
+		}
+
+		public virtual void RenderItemContent (HtmlTextWriter writer, MenuItem item, bool isDynamicItem)
+		{
+			Menu owner = Owner;
+			
+			if (!String.IsNullOrEmpty (item.ImageUrl)) {
+				writer.AddAttribute (HtmlTextWriterAttribute.Src, owner.ResolveClientUrl (item.ImageUrl));
+				writer.AddAttribute (HtmlTextWriterAttribute.Alt, item.ToolTip);
+				writer.AddStyleAttribute (HtmlTextWriterStyle.BorderStyle, "none");
+				writer.AddStyleAttribute (HtmlTextWriterStyle.VerticalAlign, "middle");
+				writer.RenderBeginTag (HtmlTextWriterTag.Img);
+				writer.RenderEndTag ();	// IMG
+			}
+
+			string format;
+			if (isDynamicItem && (format = owner.DynamicItemFormatString).Length > 0)
+				writer.Write (String.Format (format, item.Text));
+			else if (!isDynamicItem && (format = owner.StaticItemFormatString).Length > 0)
+				writer.Write (String.Format (format, item.Text));
+			else
+				writer.Write (item.Text);
+		}
+
+		public void AddCssClass (Style style, string cssClass)
+		{
+			style.AddCssClass (cssClass);
+		}
+		
+		public string GetItemClientId (string ownerClientID, MenuItem item, string suffix)
+		{
+			return ownerClientID + "_" + item.Path + suffix;
+		}
+
+		public virtual void RenderItemHref (Menu owner, HtmlTextWriter writer, MenuItem item)
+		{
+			if (!item.BranchEnabled)
+				writer.AddAttribute ("disabled", "true", false);
+			else if (!item.Selectable) {
+				writer.AddAttribute ("href", "#", false);
+				writer.AddStyleAttribute ("cursor", "text");
+			} else if (item.NavigateUrl != String.Empty) {
+				string target = item.Target != String.Empty ? item.Target : owner.Target;
+#if TARGET_J2EE
+				string navUrl = owner.ResolveClientUrl (item.NavigateUrl, String.Compare (target, "_blank", StringComparison.InvariantCultureIgnoreCase) != 0);
+#else
+				string navUrl = owner.ResolveClientUrl (item.NavigateUrl);
+#endif
+				writer.AddAttribute ("href", navUrl);
+				if (target != String.Empty)
+					writer.AddAttribute ("target", target);
+			} else
+				writer.AddAttribute ("href", GetClientEvent (owner, item));
+		}
+
+		public string GetPopOutImage (Menu owner, MenuItem item, bool isDynamicItem)
+		{
+			if (owner == null)
+				owner = Owner;
+			
+			if (item.PopOutImageUrl != String.Empty)
+				return item.PopOutImageUrl;
+
+			bool needArrowResource = false;
+			if (isDynamicItem) {
+				if (owner.DynamicPopOutImageUrl != String.Empty)
+					return owner.DynamicPopOutImageUrl;
+				if (owner.DynamicEnableDefaultPopOutImage)
+					needArrowResource = true;		
+			} else {
+				if (owner.StaticPopOutImageUrl != String.Empty)
+					return owner.StaticPopOutImageUrl;
+				if (owner.StaticEnableDefaultPopOutImage)
+					needArrowResource = true;
+			}
+
+			if (needArrowResource)
+				return GetArrowResourceUrl (owner);
+			
+			return null;
+		}
+
+		public string GetArrowResourceUrl (Menu owner) 
+		{
+			Page page = owner.Page;
+			ClientScriptManager csm = page != null ? page.ClientScript : null;
+			if (csm != null)
+				return csm.GetWebResourceUrl (typeof (Menu), "arrow_plus.gif");
+
+			return null;
+		}
+		
+		public void FillMenuStyle (HtmlHead header, bool dynamic, int menuLevel, SubMenuStyle style)
+		{
+			Menu owner = Owner;
+			if (header == null) {
+				Page page = owner.Page;
+				header = page != null ? page.Header : null;
+			}
+			
+			SubMenuStyle staticMenuStyle = owner.StaticMenuStyleInternal;
+			MenuItemStyle dynamicMenuItemStyle = owner.DynamicMenuItemStyleInternal;
+			SubMenuStyle dynamicMenuStyle = owner.DynamicMenuStyleInternal;
+			SubMenuStyleCollection levelSubMenuStyles = owner.LevelSubMenuStylesInternal;
+			
+			if (header != null) {
+				// styles are registered
+				if (!dynamic && staticMenuStyle != null) {
+					AddCssClass (style, staticMenuStyle.CssClass);
+					AddCssClass (style, staticMenuStyle.RegisteredCssClass);
+				}
+				if (dynamic && dynamicMenuStyle != null) {
+					AddCssClass (style, dynamicMenuStyle.CssClass);
+					AddCssClass (style, dynamicMenuStyle.RegisteredCssClass);
+				}
+				if (levelSubMenuStyles != null && levelSubMenuStyles.Count > menuLevel) {
+					AddCssClass (style, levelSubMenuStyles [menuLevel].CssClass);
+					AddCssClass (style, levelSubMenuStyles [menuLevel].RegisteredCssClass);
+				}
+			} else {
+				// styles are not registered
+				if (!dynamic && staticMenuStyle != null)
+					style.CopyFrom (staticMenuStyle);
+				if (dynamic && dynamicMenuStyle != null)
+					style.CopyFrom (dynamicMenuStyle);
+				if (levelSubMenuStyles != null && levelSubMenuStyles.Count > menuLevel)
+					style.CopyFrom (levelSubMenuStyles [menuLevel]);
+			}
+		}
+
+		public void RegisterStyle (Style baseStyle, Style linkStyle, HtmlHead head)
+		{
+			RegisterStyle (baseStyle, linkStyle, null, head);
+		}
+
+		public void RegisterStyle (Style baseStyle, Style linkStyle, string className, HtmlHead head)
+		{
+			if (head == null)
+				return;
+			
+			linkStyle.CopyTextStylesFrom (baseStyle);
+			linkStyle.BorderStyle = BorderStyle.None;
+			RegisterStyle (linkStyle, className, head);
+			RegisterStyle (baseStyle, className, head);
+		}
+		
+		public void RegisterStyle (Style baseStyle, HtmlHead head)
+		{
+			RegisterStyle (baseStyle, (string)null, head);
+		}
+
+		public void RegisterStyle (Style baseStyle, string className, HtmlHead head)
+		{
+			if (head == null)
+				return;
+			if (String.IsNullOrEmpty (className))
+				className = IncrementStyleClassName ();
+			baseStyle.SetRegisteredCssClass (className);
+			head.StyleSheet.CreateStyleRule (baseStyle, Owner, "." + className);
+		}
+
+		public void RenderSeparatorImage (Menu owner, HtmlTextWriter writer, string url, bool standardsCompliant)
+		{
+			if (String.IsNullOrEmpty (url))
+				return;
+			
+			writer.AddAttribute (HtmlTextWriterAttribute.Src, owner.ResolveClientUrl (url));
+			if (standardsCompliant) {
+				writer.AddAttribute (HtmlTextWriterAttribute.Alt, String.Empty);
+				writer.AddAttribute (HtmlTextWriterAttribute.Class, "separator");
+			}
+			
+			writer.RenderBeginTag (HtmlTextWriterTag.Img);
+			writer.RenderEndTag ();
+		}
+		
+		public bool IsDynamicItem (MenuItem item)
+		{
+			return IsDynamicItem (Owner, item);
+		}
+		
+		string GetClientEvent (Menu owner, MenuItem item)
+		{
+			if (owner == null)
+				owner = Owner;
+
+			Page page = owner.Page;
+			ClientScriptManager csm = page != null ? page.ClientScript : null;
+
+			if (csm == null)
+				return String.Empty;
+			
+			return csm.GetPostBackClientHyperlink (owner, item.Path, true);
+		}
+
+		string IncrementStyleClassName ()
+		{
+			registeredStylesCounter++;
+			return Owner.ClientID + "_" + registeredStylesCounter;
+		}
+	}
+}

+ 55 - 0
mcs/class/System.Web/System.Web.UI.WebControls/IMenuRenderer.cs

@@ -0,0 +1,55 @@
+//
+// Authors:
+//	Marek Habersack <[email protected]>
+//
+// (C) 2004-2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Text;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.HtmlControls;
+
+namespace System.Web.UI.WebControls
+{
+	interface IMenuRenderer
+	{
+		HtmlTextWriterTag Tag { get; }
+		
+		void AddAttributesToRender (HtmlTextWriter writer);
+		void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script);
+		void RenderBeginTag (HtmlTextWriter writer, string skipLinkText);
+		void RenderEndTag (HtmlTextWriter writer);
+		void RenderContents (HtmlTextWriter writer);
+		
+		void RenderItemContent (HtmlTextWriter writer, MenuItem item, bool isDynamicItem);
+		void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel);
+		void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast);
+		void RenderMenuEndTag (HtmlTextWriter writer, bool dynamic, int menuLevel);
+		void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool notLast, bool isFirst);
+
+		bool IsDynamicItem (MenuItem item);
+		bool IsDynamicItem (Menu owner, MenuItem item);
+	}
+}

+ 183 - 626
mcs/class/System.Web/System.Web.UI.WebControls/Menu.cs

@@ -5,7 +5,7 @@
 //	Lluis Sanchez Gual ([email protected])
 //	Igor Zelmanovich ([email protected])
 //
-// (C) 2004 Novell, Inc (http://www.novell.com)
+// (C) 2004-2010 Novell, Inc (http://www.novell.com)
 //
 
 //
@@ -36,6 +36,7 @@ using System.Collections;
 using System.Text;
 using System.ComponentModel;
 using System.Web.UI;
+using System.Web.UI.HtmlControls;
 using System.Web.Handlers;
 using System.Collections.Specialized;
 using System.IO;
@@ -50,6 +51,8 @@ namespace System.Web.UI.WebControls
 	[SupportsEventValidation]
 	public class Menu : HierarchicalDataBoundControl, IPostBackEventHandler, INamingContainer
 	{
+		IMenuRenderer renderer;
+		
 		MenuItemStyle dynamicMenuItemStyle;
 		SubMenuStyle dynamicMenuStyle;
 		MenuItemStyle dynamicSelectedStyle;
@@ -74,7 +77,6 @@ namespace System.Web.UI.WebControls
 		Hashtable _menuItemControls;
 		bool _requiresChildControlsDataBinding;
 		SiteMapNode _currSiteMapNode;
-		int registeredStylesCounter = -1;
 		List<Style> levelSelectedLinkStyles;
 		List<Style> levelMenuItemLinkStyles;
 		Style popOutBoxStyle;
@@ -86,6 +88,7 @@ namespace System.Web.UI.WebControls
 		Style dynamicHoverLinkStyle;
 		Style staticHoverLinkStyle;
 #if NET_4_0
+		bool? renderList;
 		bool includeStyleBlock = true;
 		MenuRenderingMode renderingMode = MenuRenderingMode.Default;
 #endif
@@ -119,7 +122,41 @@ namespace System.Web.UI.WebControls
 				if (eh != null) eh (this, e);
 			}
 		}
+
+		IMenuRenderer Renderer {
+			get {
+				if (renderer == null)
+					renderer = CreateRenderer (null);
+				
+				return renderer;
+			}
+		}
 #if NET_4_0
+		bool RenderList {
+			get {
+				if (renderList == null) {
+					switch (RenderingMode) {
+						case MenuRenderingMode.List:
+							renderList = true;
+							break;
+
+						case MenuRenderingMode.Table:
+							renderList = false;
+							break;
+
+						default:
+							if (RenderingCompatibilityLessThan40)
+								renderList = false;
+							else
+								renderList = true;
+							break;
+					}
+				}
+
+				return renderList.Value;
+			}
+		}
+		
 		[DefaultValue (true)]
 		[Description ("Determines whether or not to render the inline style block (only used in standards compliance mode)")]
 		public bool IncludeStyleBlock {
@@ -135,6 +172,7 @@ namespace System.Web.UI.WebControls
 					throw new ArgumentOutOfRangeException ("value");
 
 				renderingMode = value;
+				renderer = CreateRenderer (renderer);
 			}
 		}
 #endif
@@ -173,8 +211,9 @@ namespace System.Web.UI.WebControls
 		public string DynamicBottomSeparatorImageUrl {
 			get {
 				object o = ViewState ["dbsiu"];
-				if (o != null) return (string)o;
-				return "";
+				if (o != null)
+					return (string)o;
+				return String.Empty;
 			}
 			set {
 				ViewState["dbsiu"] = value;
@@ -396,7 +435,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style PopOutBoxStyle {
+		internal Style PopOutBoxStyle {
 			get {
 				if (popOutBoxStyle == null) {
 					popOutBoxStyle = new Style ();
@@ -406,7 +445,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style ControlLinkStyle {
+		internal Style ControlLinkStyle {
 			get {
 				if (controlLinkStyle == null) {
 					controlLinkStyle = new Style ();
@@ -416,7 +455,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style DynamicMenuItemLinkStyle {
+		internal Style DynamicMenuItemLinkStyle {
 			get {
 				if (dynamicMenuItemLinkStyle == null) {
 					dynamicMenuItemLinkStyle = new Style ();
@@ -425,7 +464,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style StaticMenuItemLinkStyle {
+		internal Style StaticMenuItemLinkStyle {
 			get {
 				if (staticMenuItemLinkStyle == null) {
 					staticMenuItemLinkStyle = new Style ();
@@ -434,7 +473,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style DynamicSelectedLinkStyle {
+		internal Style DynamicSelectedLinkStyle {
 			get {
 				if (dynamicSelectedLinkStyle == null) {
 					dynamicSelectedLinkStyle = new Style ();
@@ -443,7 +482,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style StaticSelectedLinkStyle {
+		internal Style StaticSelectedLinkStyle {
 			get {
 				if (staticSelectedLinkStyle == null) {
 					staticSelectedLinkStyle = new Style ();
@@ -452,7 +491,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style DynamicHoverLinkStyle {
+		internal Style DynamicHoverLinkStyle {
 			get {
 				if (dynamicHoverLinkStyle == null) {
 					dynamicHoverLinkStyle = new Style ();
@@ -461,7 +500,7 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		Style StaticHoverLinkStyle {
+		internal Style StaticHoverLinkStyle {
 			get {
 				if (staticHoverLinkStyle == null) {
 					staticHoverLinkStyle = new Style ();
@@ -470,6 +509,58 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
+		internal MenuItemStyle StaticMenuItemStyleInternal {
+			get { return staticMenuItemStyle; }
+		}
+
+		internal SubMenuStyle StaticMenuStyleInternal {
+			get { return staticMenuStyle; }
+		}
+
+		internal MenuItemStyle DynamicMenuItemStyleInternal {
+			get { return dynamicMenuItemStyle; }
+		}
+
+		internal SubMenuStyle DynamicMenuStyleInternal {
+			get { return dynamicMenuStyle; }
+		}
+
+		internal MenuItemStyleCollection LevelMenuItemStylesInternal {
+			get { return levelMenuItemStyles; }
+		}
+
+		internal List<Style> LevelMenuItemLinkStyles {
+			get { return levelMenuItemLinkStyles; }
+		}
+
+		internal SubMenuStyleCollection LevelSubMenuStylesInternal {
+			get { return levelSubMenuStyles; }
+		}
+
+		internal MenuItemStyle StaticSelectedStyleInternal {
+			get { return staticSelectedStyle; }
+		}
+
+		internal MenuItemStyle DynamicSelectedStyleInternal {
+			get { return dynamicSelectedStyle; }
+		}
+
+		internal MenuItemStyleCollection LevelSelectedStylesInternal {
+			get { return levelSelectedStyles; }
+		}
+
+		internal List<Style> LevelSelectedLinkStyles {
+			get { return levelSelectedLinkStyles; }
+		}
+
+		internal Style StaticHoverStyleInternal {
+			get { return staticHoverStyle; }
+		}
+
+		internal Style DynamicHoverStyleInternal {
+			get { return dynamicHoverStyle; }
+		}
+		
 		[PersistenceMode (PersistenceMode.InnerProperty)]
 		[NotifyParentProperty (true)]
 		[DefaultValue (null)]
@@ -805,7 +896,40 @@ namespace System.Web.UI.WebControls
 			}
 		}
 		
+		IMenuRenderer CreateRenderer (IMenuRenderer current)
+		{
+#if NET_4_0
+			Type newType = null;
+			
+			switch (RenderingMode) {
+				case MenuRenderingMode.Default:
+					if (RenderingCompatibilityLessThan40)
+						newType = typeof (MenuTableRenderer);
+					else
+						newType = typeof (MenuListRenderer);
+					break;
+					
+				case MenuRenderingMode.Table:
+					newType = typeof (MenuTableRenderer);
+					break;
 
+				case MenuRenderingMode.List:
+					newType = typeof (MenuListRenderer);
+					break;
+			}
+
+			if (newType == null)
+				return null;
+
+			if (current == null || current.GetType () != newType)
+				return Activator.CreateInstance (newType, this) as IMenuRenderer;
+#else
+			if (current == null)
+				return new MenuTableRenderer (this);
+#endif
+			return current;
+		}
+		
 		internal void SetSelectedItem (MenuItem item)
 		{
 			if (selectedItem == item) return;
@@ -964,7 +1088,7 @@ namespace System.Web.UI.WebControls
 		}
 		
 		protected override HtmlTextWriterTag TagKey {
-			get { return HtmlTextWriterTag.Table; }
+			get { return Renderer.Tag; }
 		}
 		
 		protected override void TrackViewState()
@@ -1109,9 +1233,11 @@ namespace System.Web.UI.WebControls
 			_requiresChildControlsDataBinding = true;
 		}
 
-		void CreateChildControlsForItems (MenuItemCollection items ) {
+		void CreateChildControlsForItems (MenuItemCollection items )
+		{
+			IMenuRenderer renderer = Renderer;
 			foreach (MenuItem item in items) {
-				bool isDynamicItem = IsDynamicItem (item);
+				bool isDynamicItem = renderer.IsDynamicItem (this, item);
 				if (isDynamicItem && dynamicItemTemplate != null) {
 					MenuItemTemplateContainer cter = new MenuItemTemplateContainer (item.Index, item);
 					dynamicItemTemplate.InstantiateIn (cter);
@@ -1180,94 +1306,31 @@ namespace System.Web.UI.WebControls
 			EnsureChildControls ();
 			base.OnDataBinding (e);
 		}
-
-		const string onPreRenderScript = "var {0} = new Object ();\n{0}.webForm = {1};\n{0}.disappearAfter = {2};\n{0}.vertical = {3};";
 		
 		protected internal override void OnPreRender (EventArgs e)
 		{
 			base.OnPreRender (e);
-			
-			if (!Page.ClientScript.IsClientScriptIncludeRegistered (typeof(Menu), "Menu.js")) {
-				string url = Page.ClientScript.GetWebResourceUrl (typeof(Menu), "Menu.js");
-				Page.ClientScript.RegisterClientScriptInclude (typeof(Menu), "Menu.js", url);
-			}
-			
-			string cmenu = ClientID + "_data";
-			string script = String.Format (onPreRenderScript,
-						       cmenu,
-						       Page.IsMultiForm ? Page.theForm : "window",
-						       ClientScriptManager.GetScriptLiteral (DisappearAfter),
-						       ClientScriptManager.GetScriptLiteral (Orientation == Orientation.Vertical));			
-
-			if (DynamicHorizontalOffset != 0)
-				script += String.Concat (cmenu, ".dho = ", ClientScriptManager.GetScriptLiteral (DynamicHorizontalOffset), ";\n");
-			if (DynamicVerticalOffset != 0)
-				script += String.Concat (cmenu, ".dvo = ", ClientScriptManager.GetScriptLiteral (DynamicVerticalOffset), ";\n");
-			
-			// The order in which styles are defined matters when more than one class
-			// is assigned to an element
-			RegisterStyle (PopOutBoxStyle);
-			RegisterStyle (ControlStyle, ControlLinkStyle);
-			
-			if (staticMenuItemStyle != null)
-				RegisterStyle (StaticMenuItemStyle, StaticMenuItemLinkStyle);
-
-			if (staticMenuStyle != null)
-				RegisterStyle (StaticMenuStyle);
-			
-			if (dynamicMenuItemStyle != null)
-				RegisterStyle (DynamicMenuItemStyle, DynamicMenuItemLinkStyle);
-
-			if (dynamicMenuStyle != null)
-				RegisterStyle (DynamicMenuStyle);
-
-			if (levelMenuItemStyles != null && levelMenuItemStyles.Count > 0) {
-				levelMenuItemLinkStyles = new List<Style> (levelMenuItemStyles.Count);
-				foreach (Style style in levelMenuItemStyles) {
-					Style linkStyle = new Style ();
-					levelMenuItemLinkStyles.Add (linkStyle);
-					RegisterStyle (style, linkStyle);
-				}
-			}
-		
-			if (levelSubMenuStyles != null)
-				foreach (Style style in levelSubMenuStyles)
-					RegisterStyle (style);
 
-			if (staticSelectedStyle != null)
-				RegisterStyle (staticSelectedStyle, StaticSelectedLinkStyle);
-			
-			if (dynamicSelectedStyle != null)
-				RegisterStyle (dynamicSelectedStyle, DynamicSelectedLinkStyle);
-
-			if (levelSelectedStyles != null && levelSelectedStyles.Count > 0) {
-				levelSelectedLinkStyles = new List<Style> (levelSelectedStyles.Count);
-				foreach (Style style in levelSelectedStyles) {
-					Style linkStyle = new Style ();
-					levelSelectedLinkStyles.Add (linkStyle);
-					RegisterStyle (style, linkStyle);
-				}
-			}
-			
-			if (staticHoverStyle != null) {
-				if (Page.Header == null)
-					throw new InvalidOperationException ("Using Menu.StaticHoverStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
-				RegisterStyle (staticHoverStyle, StaticHoverLinkStyle);
-				script += string.Concat (cmenu, ".staticHover = ", ClientScriptManager.GetScriptLiteral (staticHoverStyle.RegisteredCssClass), ";\n");
-				script += string.Concat (cmenu, ".staticLinkHover = ", ClientScriptManager.GetScriptLiteral (StaticHoverLinkStyle.RegisteredCssClass), ";\n");
+			string cmenu = ClientID + "_data";
+			StringBuilder script = new StringBuilder ();
+			Page page = Page;
+			HtmlHead header;
+			ClientScriptManager csm;
+
+			if (page != null) {
+				header = page.Header;
+				csm = page.ClientScript;
+			} else {
+				header = null;
+				csm = null;
 			}
 			
-			if (dynamicHoverStyle != null) {
-				if (Page.Header == null)
-					throw new InvalidOperationException ("Using Menu.DynamicHoverStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
-				RegisterStyle (dynamicHoverStyle, DynamicHoverLinkStyle);
-				script += string.Concat (cmenu, ".dynamicHover = ", ClientScriptManager.GetScriptLiteral (dynamicHoverStyle.RegisteredCssClass), ";\n");
-				script += string.Concat (cmenu, ".dynamicLinkHover = ", ClientScriptManager.GetScriptLiteral (DynamicHoverLinkStyle.RegisteredCssClass), ";\n");
-			}
-
-			Page.ClientScript.RegisterWebFormClientScript ();
-			Page.ClientScript.RegisterStartupScript (typeof(Menu), ClientID, script, true);
+			Renderer.PreRender (page, header, csm, cmenu, script);
 
+			if (csm != null) {
+				csm.RegisterWebFormClientScript ();
+				csm.RegisterStartupScript (typeof(Menu), ClientID, script.ToString (), true);
+			}
 		}
 
 		void InitializeDataBindings () {
@@ -1281,27 +1344,6 @@ namespace System.Web.UI.WebControls
 			else
 				bindings = null;
 		}
-
-		string IncrementStyleClassName () {
-			registeredStylesCounter++;
-			return ClientID + "_" + registeredStylesCounter;
-		}
-
-		void RegisterStyle (Style baseStyle, Style linkStyle) {
-			linkStyle.CopyTextStylesFrom (baseStyle);
-			linkStyle.BorderStyle = BorderStyle.None;
-			RegisterStyle (linkStyle);
-			RegisterStyle (baseStyle);
-		}
-
-		void RegisterStyle (Style baseStyle)
-		{
-			if (Page.Header == null)
-				return;
-			string className = IncrementStyleClassName ();
-			baseStyle.SetRegisteredCssClass (className);
-			Page.Header.StyleSheet.CreateStyleRule (baseStyle, this, "." + className);
-		}
 		
 		protected internal override void Render (HtmlTextWriter writer)
 		{
@@ -1311,63 +1353,15 @@ namespace System.Web.UI.WebControls
 		
 		protected override void AddAttributesToRender (HtmlTextWriter writer)
 		{
-			writer.AddAttribute ("cellpadding", "0", false);
-			writer.AddAttribute ("cellspacing", "0", false);
-			writer.AddAttribute ("border", "0", false);
-			if (Page.Header != null) {
-				// styles are registered
-				if (staticMenuStyle != null) {
-					AddCssClass (ControlStyle, staticMenuStyle.CssClass);
-					AddCssClass (ControlStyle, staticMenuStyle.RegisteredCssClass);
-				}
-				if (levelSubMenuStyles != null && levelSubMenuStyles.Count > 0) {
-					AddCssClass (ControlStyle, levelSubMenuStyles [0].CssClass);
-					AddCssClass (ControlStyle, levelSubMenuStyles [0].RegisteredCssClass);
-				}
-			}
-			else {
-				// styles are not registered
-				if (staticMenuStyle != null){
-					ControlStyle.CopyFrom (staticMenuStyle);
-				}
-				if (levelSubMenuStyles != null && levelSubMenuStyles.Count > 0) {
-					ControlStyle.CopyFrom (levelSubMenuStyles [0]);
-				}
-			}
+			Renderer.AddAttributesToRender (writer);
 			base.AddAttributesToRender (writer);
 		}
-
-		void AddCssClass (Style style, string cssClass) {
-			style.AddCssClass (cssClass);
-		}
 		
 		public override void RenderBeginTag (HtmlTextWriter writer)
 		{
 			string skipLinkText = SkipLinkText;
-			if (!String.IsNullOrEmpty (skipLinkText)) {
-				// <a href="#ID_SkipLink">
-				writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + ClientID + "_SkipLink");
-				writer.RenderBeginTag (HtmlTextWriterTag.A);
-				
-				// <img alt="" height="0" width="0" src="" style="border-width:0px;"/>
-				writer.AddAttribute (HtmlTextWriterAttribute.Alt, skipLinkText);
-				writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
-				writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
-				
-				Page page = Page;
-				ClientScriptManager csm;
-				
-				if (page != null)
-					csm = page.ClientScript;
-				else
-					csm = new ClientScriptManager (null);
-				writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
-				writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
-				writer.RenderBeginTag (HtmlTextWriterTag.Img);
-				writer.RenderEndTag ();
-				
-				writer.RenderEndTag (); // </a>
-			}
+			if (!String.IsNullOrEmpty (skipLinkText))
+				Renderer.RenderBeginTag (writer, skipLinkText);
 			base.RenderBeginTag (writer);
 		}
 		
@@ -1375,12 +1369,11 @@ namespace System.Web.UI.WebControls
 		{
 			base.RenderEndTag (writer);
 
-			if (StaticDisplayLevels == 1 && MaximumDynamicDisplayLevels > 0)
-				RenderDynamicMenu (writer, Items);
-
+			Renderer.RenderEndTag (writer);
+			
 			string skipLinkText = SkipLinkText;
 			if (!String.IsNullOrEmpty (skipLinkText)) {
-				writer.AddAttribute (HtmlTextWriterAttribute.Id, "SkipLink");
+				writer.AddAttribute (HtmlTextWriterAttribute.Id, ClientID + "_SkipLink");
 				writer.RenderBeginTag (HtmlTextWriterTag.A);
 				writer.RenderEndTag ();
 			}
@@ -1388,10 +1381,11 @@ namespace System.Web.UI.WebControls
 		
 		protected internal override void RenderContents (HtmlTextWriter writer)
 		{
-			RenderMenuBody (writer, Items, Orientation == Orientation.Vertical, false, false);
+			Renderer.RenderContents (writer);
 		}
 
-		void RenderDynamicMenu (HtmlTextWriter writer, MenuItemCollection items) {
+		internal void RenderDynamicMenu (HtmlTextWriter writer, MenuItemCollection items)
+		{
 			for (int n = 0; n < items.Count; n++) {
 				if (DisplayChildren (items [n])) {
 					RenderDynamicMenu (writer, items [n]);
@@ -1545,108 +1539,22 @@ namespace System.Web.UI.WebControls
 			}
 		}
 
-		void RenderMenuBeginTagAttributes (HtmlTextWriter writer, bool dynamic, int menuLevel) {
-			writer.AddAttribute ("cellpadding", "0", false);
-			writer.AddAttribute ("cellspacing", "0", false);
-			writer.AddAttribute ("border", "0", false);
-
-			if (!dynamic) {
-				SubMenuStyle style = new SubMenuStyle ();
-				FillMenuStyle (dynamic, menuLevel, style);
-				style.AddAttributesToRender (writer);
-			}
-		}
-
-		void FillMenuStyle (bool dynamic, int menuLevel, SubMenuStyle style) {
-			if (Page.Header != null) {
-				// styles are registered
-				if (!dynamic && staticMenuStyle != null) {
-					AddCssClass (style, staticMenuStyle.CssClass);
-					AddCssClass (style, staticMenuStyle.RegisteredCssClass);
-				}
-				if (dynamic && dynamicMenuStyle != null) {
-					AddCssClass (style, dynamicMenuStyle.CssClass);
-					AddCssClass (style, dynamicMenuStyle.RegisteredCssClass);
-				}
-				if (levelSubMenuStyles != null && levelSubMenuStyles.Count > menuLevel) {
-					AddCssClass (style, levelSubMenuStyles [menuLevel].CssClass);
-					AddCssClass (style, levelSubMenuStyles [menuLevel].RegisteredCssClass);
-				}
-			}
-			else {
-				// styles are not registered
-				if (!dynamic && staticMenuStyle != null) {
-					style.CopyFrom (staticMenuStyle);
-				}
-				if (dynamic && dynamicMenuStyle != null) {
-					style.CopyFrom (dynamicMenuStyle);
-				}
-				if (levelSubMenuStyles != null && levelSubMenuStyles.Count > menuLevel) {
-					style.CopyFrom (levelSubMenuStyles [menuLevel]);
-				}
-			}
-		}
-
-		void RenderMenu (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, int menuLevel, bool notLast)
-		{
-			RenderMenuBeginTag (writer, dynamic, menuLevel);
-			RenderMenuBody (writer, items, vertical, dynamic, notLast);
-			RenderMenuEndTag (writer);
-		}
-		
-		void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
-		{
-			RenderMenuBeginTagAttributes (writer, dynamic, menuLevel);
-			writer.RenderBeginTag (HtmlTextWriterTag.Table);
-		}
-		
-		void RenderMenuEndTag (HtmlTextWriter writer)
+		internal void RenderMenu (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, int menuLevel, bool notLast)
 		{
-			writer.RenderEndTag ();
-		}
-
-		void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast) {
-			if (!vertical)
-				writer.RenderBeginTag (HtmlTextWriterTag.Tr);
-
-			int count = items.Count;
-			for (int n = 0; n < count; n++) {
-				MenuItem item = items [n];
-				Adapters.MenuAdapter adapter = Adapter as Adapters.MenuAdapter;
-				if (adapter != null)
-					adapter.RenderItem (writer, item, n);
-				else
-					RenderMenuItem (writer, item, (n + 1 == count) ? notLast : true, n == 0);
-			}
-
-			if (!vertical)
-				writer.RenderEndTag ();	// TR
-		}
-
-		void RenderMenuItemSpacing (HtmlTextWriter writer, Unit itemSpacing, bool vertical) {
-			if (vertical) {
-				writer.AddStyleAttribute ("height", itemSpacing.ToString ());
-				writer.RenderBeginTag (HtmlTextWriterTag.Tr);
-				writer.RenderBeginTag (HtmlTextWriterTag.Td);
-				writer.RenderEndTag ();
-				writer.RenderEndTag ();
-			}
-			else {
-				writer.AddStyleAttribute ("width", itemSpacing.ToString ());
-				writer.RenderBeginTag (HtmlTextWriterTag.Td);
-				writer.RenderEndTag ();
-			}
-		}
-		
-		bool IsDynamicItem (MenuItem item) {
-			return item.Depth + 1 > StaticDisplayLevels;
+			IMenuRenderer renderer = Renderer;
+			
+			renderer.RenderMenuBeginTag (writer, dynamic, menuLevel);
+			renderer.RenderMenuBody (writer, items, vertical, dynamic, notLast);
+			renderer.RenderMenuEndTag (writer, dynamic, menuLevel);
 		}
 
-		bool DisplayChildren (MenuItem item) {
+		internal bool DisplayChildren (MenuItem item)
+		{
 			return (item.Depth + 1 < StaticDisplayLevels + MaximumDynamicDisplayLevels) && item.ChildItems.Count > 0;
 		}
 		
-		internal void RenderItem (HtmlTextWriter writer, MenuItem item, int position) {
+		internal void RenderItem (HtmlTextWriter writer, MenuItem item, int position)
+		{
 			// notLast should be true if item or any of its ancestors is not a
 			// last child.
 			bool notLast = false;
@@ -1659,314 +1567,18 @@ namespace System.Web.UI.WebControls
 				}
 				child = parent;
 			}
-			
-			RenderMenuItem (writer, item, notLast, position == 0);
-		}
-		
-		void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool notLast, bool isFirst) {
-			bool displayChildren = DisplayChildren (item);
-			bool dynamicChildren = displayChildren && (item.Depth + 1 >= StaticDisplayLevels);
-			bool isDynamicItem = IsDynamicItem (item);
-			bool vertical = (Orientation == Orientation.Vertical) || isDynamicItem;
-			
-			Unit itemSpacing = GetItemSpacing (item, isDynamicItem);
-
-			if (itemSpacing != Unit.Empty && (item.Depth > 0 || !isFirst))
-				RenderMenuItemSpacing (writer, itemSpacing, vertical);
-
-			if(!String.IsNullOrEmpty(item.ToolTip))
-				writer.AddAttribute (HtmlTextWriterAttribute.Title, item.ToolTip);
-			if (vertical)
-				writer.RenderBeginTag (HtmlTextWriterTag.Tr);
-
-			string parentId = isDynamicItem ? "'" + item.Parent.Path + "'" : "null";
-			if (dynamicChildren) {
-				writer.AddAttribute ("onmouseover",
-						     "javascript:Menu_OverItem ('" + ClientID + "','" + item.Path + "'," + parentId + ")");
-				writer.AddAttribute ("onmouseout",
-						     "javascript:Menu_OutItem ('" + ClientID + "','" + item.Path + "')");
-			} else if (isDynamicItem) {
-				writer.AddAttribute ("onmouseover",
-						     "javascript:Menu_OverDynamicLeafItem ('" + ClientID + "','" + item.Path + "'," + parentId + ")");
-				writer.AddAttribute ("onmouseout",
-						     "javascript:Menu_OutItem ('" + ClientID + "','" + item.Path + "'," + parentId + ")");
-			} else {
-				writer.AddAttribute ("onmouseover",
-						     "javascript:Menu_OverStaticLeafItem ('" + ClientID + "','" + item.Path + "')");
-				writer.AddAttribute ("onmouseout",
-						     "javascript:Menu_OutItem ('" + ClientID + "','" + item.Path + "')");
-			}
-
-			writer.RenderBeginTag (HtmlTextWriterTag.Td);
-
-			// Top separator image
-
-			if (isDynamicItem && DynamicTopSeparatorImageUrl != "") {
-				writer.AddAttribute ("src", ResolveClientUrl (DynamicTopSeparatorImageUrl));
-				writer.RenderBeginTag (HtmlTextWriterTag.Img);
-				writer.RenderEndTag ();	// IMG
-			}
-			else if (!isDynamicItem && StaticTopSeparatorImageUrl != "") {
-				writer.AddAttribute ("src", ResolveClientUrl (StaticTopSeparatorImageUrl));
-				writer.RenderBeginTag (HtmlTextWriterTag.Img);
-				writer.RenderEndTag ();	// IMG
-			}
-
-			// Menu item box
-			
-			MenuItemStyle style = new MenuItemStyle ();
-			if (Page.Header != null) {
-				// styles are registered
-				if (!isDynamicItem && staticMenuItemStyle != null) {
-					AddCssClass (style, staticMenuItemStyle.CssClass);
-					AddCssClass (style, staticMenuItemStyle.RegisteredCssClass);
-				}
-				if (isDynamicItem && dynamicMenuItemStyle != null) {
-					AddCssClass (style, dynamicMenuItemStyle.CssClass);
-					AddCssClass (style, dynamicMenuItemStyle.RegisteredCssClass);
-				}
-				if (levelMenuItemStyles != null && levelMenuItemStyles.Count > item.Depth) {
-					AddCssClass (style, levelMenuItemStyles [item.Depth].CssClass);
-					AddCssClass (style, levelMenuItemStyles [item.Depth].RegisteredCssClass);
-				}
-				if (item == SelectedItem) {
-					if (!isDynamicItem && staticSelectedStyle != null) {
-						AddCssClass (style, staticSelectedStyle.CssClass);
-						AddCssClass (style, staticSelectedStyle.RegisteredCssClass);
-					}
-					if (isDynamicItem && dynamicSelectedStyle != null) {
-						AddCssClass (style, dynamicSelectedStyle.CssClass);
-						AddCssClass (style, dynamicSelectedStyle.RegisteredCssClass);
-					}
-					if (levelSelectedStyles != null && levelSelectedStyles.Count > item.Depth) {
-						AddCssClass (style, levelSelectedStyles [item.Depth].CssClass);
-						AddCssClass (style, levelSelectedStyles [item.Depth].RegisteredCssClass);
-					}
-				}
-			}
-			else {
-				// styles are not registered
-				if (!isDynamicItem && staticMenuItemStyle != null) {
-					style.CopyFrom (staticMenuItemStyle);
-				}
-				if (isDynamicItem && dynamicMenuItemStyle != null) {
-					style.CopyFrom (dynamicMenuItemStyle);
-				}
-				if (levelMenuItemStyles != null && levelMenuItemStyles.Count > item.Depth) {
-					style.CopyFrom (levelMenuItemStyles [item.Depth]);
-				}
-				if (item == SelectedItem) {
-					if (!isDynamicItem && staticSelectedStyle != null) {
-						style.CopyFrom (staticSelectedStyle);
-					}
-					if (isDynamicItem && dynamicSelectedStyle != null) {
-						style.CopyFrom (dynamicSelectedStyle);
-					}
-					if (levelSelectedStyles != null && levelSelectedStyles.Count > item.Depth) {
-						style.CopyFrom (levelSelectedStyles [item.Depth]);
-					}
-				}
-			}
-			style.AddAttributesToRender (writer);
-
-			writer.AddAttribute ("id", GetItemClientId (item, "i"));
-
-			writer.AddAttribute ("cellpadding", "0", false);
-			writer.AddAttribute ("cellspacing", "0", false);
-			writer.AddAttribute ("border", "0", false);
-			writer.AddAttribute ("width", "100%", false);
-			writer.RenderBeginTag (HtmlTextWriterTag.Table);
-			writer.RenderBeginTag (HtmlTextWriterTag.Tr);
-
-			// Menu item text
-
-			if (vertical)
-				writer.AddStyleAttribute (HtmlTextWriterStyle.Width, "100%");
-			if (!ItemWrap)
-				writer.AddStyleAttribute ("white-space", "nowrap");
-			writer.RenderBeginTag (HtmlTextWriterTag.Td);
-
-			RenderItemHref (writer, item);
-			
-			Style linkStyle = new Style ();
-			if (Page.Header != null) {
-				// styles are registered
-				AddCssClass (linkStyle, ControlLinkStyle.RegisteredCssClass);
-
-				if (!isDynamicItem && staticMenuItemStyle != null) {
-					AddCssClass (linkStyle, staticMenuItemStyle.CssClass);
-					AddCssClass (linkStyle, staticMenuItemLinkStyle.RegisteredCssClass);
-				}
-				if (isDynamicItem && dynamicMenuItemStyle != null) {
-					AddCssClass (linkStyle, dynamicMenuItemStyle.CssClass);
-					AddCssClass (linkStyle, dynamicMenuItemLinkStyle.RegisteredCssClass);
-				}
-				if (levelMenuItemStyles != null && levelMenuItemStyles.Count > item.Depth) {
-					AddCssClass (linkStyle, levelMenuItemStyles [item.Depth].CssClass);
-					AddCssClass (linkStyle, levelMenuItemLinkStyles [item.Depth].RegisteredCssClass);
-				}
-				if (item == SelectedItem) {
-					if (!isDynamicItem && staticSelectedStyle != null) {
-						AddCssClass (linkStyle, staticSelectedStyle.CssClass);
-						AddCssClass (linkStyle, staticSelectedLinkStyle.RegisteredCssClass);
-					}
-					if (isDynamicItem && dynamicSelectedStyle != null) {
-						AddCssClass (linkStyle, dynamicSelectedStyle.CssClass);
-						AddCssClass (linkStyle, dynamicSelectedLinkStyle.RegisteredCssClass);
-					}
-					if (levelSelectedStyles != null && levelSelectedStyles.Count > item.Depth) {
-						AddCssClass (linkStyle, levelSelectedStyles [item.Depth].CssClass);
-						AddCssClass (linkStyle, levelSelectedLinkStyles [item.Depth].RegisteredCssClass);
-					}
-				}
-			}
-			else {
-				// styles are not registered
-				linkStyle.CopyFrom (ControlLinkStyle);
-
-				if (!isDynamicItem && staticMenuItemStyle != null) {
-					linkStyle.CopyFrom (staticMenuItemLinkStyle);
-				}
-				if (isDynamicItem && dynamicMenuItemStyle != null) {
-					linkStyle.CopyFrom (dynamicMenuItemLinkStyle);
-				}
-				if (levelMenuItemStyles != null && levelMenuItemStyles.Count > item.Depth) {
-					linkStyle.CopyFrom (levelMenuItemLinkStyles [item.Depth]);
-				}
-				if (item == SelectedItem) {
-					if (!isDynamicItem && staticSelectedStyle != null) {
-						linkStyle.CopyFrom (staticSelectedLinkStyle);
-					}
-					if (isDynamicItem && dynamicSelectedStyle != null) {
-						linkStyle.CopyFrom (dynamicSelectedLinkStyle);
-					}
-					if (levelSelectedStyles != null && levelSelectedStyles.Count > item.Depth) {
-						linkStyle.CopyFrom (levelSelectedLinkStyles [item.Depth]);
-					}
-				}
-
-				linkStyle.AlwaysRenderTextDecoration = true;
-			}
-			linkStyle.AddAttributesToRender (writer);
-
-			writer.AddAttribute ("id", GetItemClientId (item, "l"));
-			
-			if (item.Depth > 0 && !isDynamicItem) {
-				double value;
-#if NET_4_0
-				Unit unit = StaticSubMenuIndent;
-				if (unit == Unit.Empty)
-					value = 16;
-				else
-					value = unit.Value;
-#else
-				value = StaticSubMenuIndent.Value;
-#endif
-				Unit indent = new Unit (value * item.Depth, StaticSubMenuIndent.Type);
-				writer.AddStyleAttribute (HtmlTextWriterStyle.MarginLeft, indent.ToString ());
-			}
-			writer.RenderBeginTag (HtmlTextWriterTag.A);
-			RenderItemContent (writer, item, isDynamicItem);
-			writer.RenderEndTag ();	// A
-
-			writer.RenderEndTag ();	// TD
-
-			// Popup image
-
-			if (dynamicChildren) {
-				string popOutImage = GetPopOutImage (item, isDynamicItem);
-				if (popOutImage != null) {
-					writer.RenderBeginTag (HtmlTextWriterTag.Td);
-					writer.AddAttribute ("src", ResolveClientUrl (popOutImage));
-					writer.AddAttribute ("border", "0");
-					string toolTip = String.Format (isDynamicItem ? DynamicPopOutImageTextFormatString : StaticPopOutImageTextFormatString, item.Text);
-					writer.AddAttribute (HtmlTextWriterAttribute.Alt, toolTip);
-					writer.RenderBeginTag (HtmlTextWriterTag.Img);
-					writer.RenderEndTag ();	// IMG
-					writer.RenderEndTag ();	// TD
-				}
-			}
-
-			writer.RenderEndTag ();	// TR
-			writer.RenderEndTag ();	// TABLE
-			
-			writer.RenderEndTag ();	// TD
-
-			if (!vertical && itemSpacing == Unit.Empty && (notLast || (displayChildren && !dynamicChildren))) {
-				writer.AddStyleAttribute ("width", "3px");
-				writer.RenderBeginTag (HtmlTextWriterTag.Td);
-				writer.RenderEndTag ();
-			}
-			
-			// Bottom separator image
-			string separatorImg = item.SeparatorImageUrl;
-			if (separatorImg.Length == 0) {
-				if (isDynamicItem)
-					separatorImg = DynamicBottomSeparatorImageUrl;
-				else
-					separatorImg = StaticBottomSeparatorImageUrl;
-			}
-			if (separatorImg.Length > 0) {
-				if (!vertical)
-					writer.RenderBeginTag (HtmlTextWriterTag.Td);
-				writer.AddAttribute ("src", ResolveClientUrl (separatorImg));
-				writer.RenderBeginTag (HtmlTextWriterTag.Img);
-				writer.RenderEndTag ();	// IMG
-				if (!vertical)
-					writer.RenderEndTag (); // TD
-			}
-
-			if (vertical)
-				writer.RenderEndTag ();	// TR
-
-			if (itemSpacing != Unit.Empty)
-				RenderMenuItemSpacing (writer, itemSpacing, vertical);
-
-			// Submenu
-
-			if (displayChildren && !dynamicChildren) {
-				if (vertical)
-					writer.RenderBeginTag (HtmlTextWriterTag.Tr);
-				writer.RenderBeginTag (HtmlTextWriterTag.Td);
-				writer.AddAttribute ("width", "100%");
-				RenderMenu (writer, item.ChildItems, Orientation == Orientation.Vertical, false, item.Depth + 1, notLast);
-				if (item.Depth + 2 == StaticDisplayLevels)
-					RenderDynamicMenu (writer, item.ChildItems);
-				writer.RenderEndTag ();	// TD
-				if (vertical)
-					writer.RenderEndTag ();	// TR
-			}
-
+			Renderer.RenderMenuItem (writer, item, notLast, position == 0);
 		}
 
-		void RenderItemContent (HtmlTextWriter writer, MenuItem item, bool isDynamicItem) {
-			if (_menuItemControls!=null && _menuItemControls [item] != null) {
+		internal void RenderItemContent (HtmlTextWriter writer, MenuItem item, bool isDynamicItem)
+		{
+			if (_menuItemControls!=null && _menuItemControls [item] != null)
 				((Control) _menuItemControls [item]).Render (writer);
-			}
-			else {
-
-				if (!String.IsNullOrEmpty (item.ImageUrl)) {
-					writer.AddAttribute (HtmlTextWriterAttribute.Src, ResolveClientUrl (item.ImageUrl));
-					writer.AddAttribute (HtmlTextWriterAttribute.Alt, item.ToolTip);
-					writer.AddStyleAttribute (HtmlTextWriterStyle.BorderStyle, "none");
-					writer.AddStyleAttribute (HtmlTextWriterStyle.VerticalAlign, "middle");
-					writer.RenderBeginTag (HtmlTextWriterTag.Img);
-					writer.RenderEndTag ();	// IMG
-				}
 
-				if (isDynamicItem && DynamicItemFormatString.Length > 0) {
-					writer.Write (String.Format (DynamicItemFormatString, item.Text));
-				}
-				else if (!isDynamicItem && StaticItemFormatString.Length > 0) {
-					writer.Write (String.Format (StaticItemFormatString, item.Text));
-				}
-				else {
-					writer.Write (item.Text);
-				}
-			}
+			Renderer.RenderItemContent (writer, item, isDynamicItem);
 		}
 			
-		Unit GetItemSpacing (MenuItem item, bool dynamic)
+		internal Unit GetItemSpacing (MenuItem item, bool dynamic)
 		{
 			Unit itemSpacing = Unit.Empty;
 			
@@ -1996,61 +1608,6 @@ namespace System.Web.UI.WebControls
 			else
 				return Unit.Empty;
 		}
-		
-		string GetPopOutImage (MenuItem item, bool isDynamicItem)
-		{
-			if (item.PopOutImageUrl != "")
-				return item.PopOutImageUrl;
-
-			if (isDynamicItem) {
-				if (DynamicPopOutImageUrl != "")
-					return DynamicPopOutImageUrl;
-				if (DynamicEnableDefaultPopOutImage)
-					return Page.ClientScript.GetWebResourceUrl (typeof (Menu), "arrow_plus.gif");
-			} else {
-				if (StaticPopOutImageUrl != "")
-					return StaticPopOutImageUrl;
-				if (StaticEnableDefaultPopOutImage)
-					return Page.ClientScript.GetWebResourceUrl (typeof (Menu), "arrow_plus.gif");
-			}
-			return null;
-		}
-			
-		void RenderItemHref (HtmlTextWriter writer, MenuItem item)
-		{
-			if (!item.BranchEnabled) {
-				writer.AddAttribute ("disabled", "true", false);
-			}
-			else if (!item.Selectable) {
-				writer.AddAttribute ("href", "#", false);
-				writer.AddStyleAttribute ("cursor", "text");
-			}
-			else if (item.NavigateUrl != "") {
-				string target = item.Target != "" ? item.Target : Target;
-#if TARGET_J2EE
-				string navUrl = ResolveClientUrl (item.NavigateUrl, String.Compare (target, "_blank", StringComparison.InvariantCultureIgnoreCase) != 0);
-#else
-				string navUrl = ResolveClientUrl (item.NavigateUrl);
-#endif
-				writer.AddAttribute ("href", navUrl);
-				if (target != "")
-					writer.AddAttribute ("target", target);
-			}
-			else {
-				writer.AddAttribute ("href", GetClientEvent (item));
-			}
-
-		}
-		
-		string GetItemClientId (MenuItem item, string sufix)
-		{
-			return ClientID + "_" + item.Path + sufix;
-		}
-		
-		string GetClientEvent (MenuItem item)
-		{
-			return Page.ClientScript.GetPostBackClientHyperlink (this, item.Path, true);
-		}
 
 		class MenuTemplateWriter : TextWriter
 		{

+ 404 - 0
mcs/class/System.Web/System.Web.UI.WebControls/MenuListRenderer.cs

@@ -0,0 +1,404 @@
+//
+// Authors:
+//	Marek Habersack <[email protected]>
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Text;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.HtmlControls;
+
+namespace System.Web.UI.WebControls
+{
+	sealed class MenuListRenderer : BaseMenuRenderer
+	{
+		bool haveDynamicPopOut;
+		
+		public override HtmlTextWriterTag Tag {
+			get { return HtmlTextWriterTag.Div; }
+			
+		}
+		
+		public MenuListRenderer (Menu owner)
+			: base (owner)
+		{
+		}
+
+		public override void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script)
+		{
+			Menu owner = Owner;
+			script.AppendFormat ("new Sys.WebForms.Menu ({{ element: '{0}', disappearAfter: {1}, orientation: '{2}', tabIndex: {3}, disabled: {4} }});",
+					     owner.ClientID,
+					     ClientScriptManager.GetScriptLiteral (owner.DisappearAfter),
+					     owner.Orientation.ToString ().ToLowerInvariant (),
+					     ClientScriptManager.GetScriptLiteral (owner.TabIndex),
+					     (!owner.Enabled).ToString ().ToLowerInvariant ());
+
+			Type mt = typeof (Menu);
+			if (!csm.IsClientScriptIncludeRegistered (mt, "MenuModern.js")) {
+				string url = csm.GetWebResourceUrl (mt, "MenuModern.js");
+				csm.RegisterClientScriptInclude (mt, "MenuModern.js", url);
+			}
+			
+			if (!owner.IncludeStyleBlock)
+				return;
+			
+			if (head == null)
+				throw new InvalidOperationException ("Using Menu.IncludeStyleBlock requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
+
+			StyleBlock block = new StyleBlock (owner.ClientID);
+			Style style = owner.ControlStyle;
+			bool horizontal = owner.Orientation == Orientation.Horizontal;
+			if (style != null)
+				block.RegisterStyle (style);
+			
+			// #MenuId img.icon { border-style:none;vertical-align:middle; }
+			block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.icon")
+				.Add (HtmlTextWriterStyle.VerticalAlign, "middle");
+
+			// #MenuId img.separator { border-style:none;display:block; }
+			block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.separator")
+				.Add (HtmlTextWriterStyle.Display, "block");
+
+			// #MenuId img.horizontal-separator { border-style:none;vertical-align:middle; }
+			if (horizontal)
+				block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.horizontal-separator")
+					.Add (HtmlTextWriterStyle.VerticalAlign, "middle");
+			
+			// #MenuId ul { list-style:none;margin:0;padding:0;width:auto; }
+			block.RegisterStyle (HtmlTextWriterStyle.ListStyleType, "none", "ul")
+				.Add (HtmlTextWriterStyle.Margin, "0")
+				.Add (HtmlTextWriterStyle.Padding, "0")
+				.Add (HtmlTextWriterStyle.Width, "auto");
+
+			SubMenuStyle sms = owner.StaticMenuStyleInternal;
+			if (sms != null) {
+				// #MenuId ul.static { ... }
+				block.RegisterStyle (sms, "ul.static");
+			}
+
+			// #MenuId ul.dynamic { ...; z-index:1; ... }
+			NamedCssStyleCollection css = block.RegisterStyle ("ul.dynamic");
+			sms = owner.DynamicMenuStyleInternal;
+			if (sms != null) {
+				sms.ForeColor = Color.Empty;
+				css.Add (sms);
+			}
+			
+			css.Add (HtmlTextWriterStyle.ZIndex, "1");
+			int num = owner.DynamicHorizontalOffset;
+			if (num != 0)
+				css.Add (HtmlTextWriterStyle.MarginLeft, num + "px");
+			num = owner.DynamicVerticalOffset;
+			if (num != 0)
+				css.Add (HtmlTextWriterStyle.MarginTop, num + "px");
+
+			// BUG: rendering of LevelSubMenuStyles throws InvalidCastException on .NET
+			// but I suspect the code it is supposed to generate is as follows:
+			//
+			// #MenuId ul.levelX { ... }
+			//
+			// so we will just ignore the bug and go with the above code.
+			RenderLevelStyles (block, num, owner.LevelSubMenuStyles, "ul.level");
+			
+			// #MenuId a { text-decoration:none;white-space:nowrap;display:block; }
+			block.RegisterStyle (HtmlTextWriterStyle.TextDecoration, "none", "a")
+				.Add (HtmlTextWriterStyle.WhiteSpace, "nowrap")
+				.Add (HtmlTextWriterStyle.Display, "block");
+
+			// #MenuId a.static { ... }
+			RenderAnchorStyle (block, owner.StaticMenuItemStyleInternal, "a.static");
+			
+			// #MenuId a.popout { background-image:url("...");background-repeat:no-repeat;background-position:right center;padding-right:14px; }
+			bool needDynamicPopOut = false;
+			string str = owner.StaticPopOutImageUrl;
+
+			css = null;
+			string urlFormat = "url(\"{0}\")";
+			if (String.IsNullOrEmpty (str)) {
+				if (owner.StaticEnableDefaultPopOutImage)
+					css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, GetArrowResourceUrl (owner)), "a.popout");
+				else
+					needDynamicPopOut = true;
+			} else {
+				css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout");
+				needDynamicPopOut = true;
+			}
+			
+			if (css != null)
+				css.Add ("background-repeat", "no-repeat")
+					.Add ("background-position", "right center")
+					.Add (HtmlTextWriterStyle.PaddingRight, "14px");
+
+			// #MenuId a.popout-dynamic { background:url("...") no-repeat right center;padding-right:14px; }
+			str = owner.DynamicPopOutImageUrl;
+			bool haveDynamicUrl = !String.IsNullOrEmpty (str);
+			css = null;
+			if (needDynamicPopOut || haveDynamicUrl) {
+				urlFormat = "url(\"{0}\") no-repeat right center";
+				if (!haveDynamicUrl) {
+					if (owner.DynamicEnableDefaultPopOutImage)
+						css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, GetArrowResourceUrl (owner)), "a.popout-dynamic");
+				} else
+					css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout-dynamic");
+			}
+			if (css != null) {
+				haveDynamicPopOut = true;
+				css.Add (HtmlTextWriterStyle.PaddingRight, "14px");
+			}
+
+			// #MenuId a.dynamic { ... }
+			RenderAnchorStyle (block, owner.DynamicMenuItemStyleInternal, "a.dynamic");
+			
+			num = owner.StaticDisplayLevels;
+			Unit ssmi = owner.StaticSubMenuIndent;
+			string unitName;
+			double indent;
+				
+			if (ssmi == Unit.Empty) {
+				unitName = "em";
+				indent = 1;
+			} else {
+				unitName = Unit.GetExtension (ssmi.Type);
+				indent = ssmi.Value;
+			}
+
+			// #MenuId a.levelX { ... }
+			RenderLevelStyles (block, num, owner.LevelMenuItemStyles, "a.level", unitName, indent);
+
+			// #MenuId a.selected.levelX { ... }
+			RenderLevelStyles (block, num, owner.LevelSelectedStyles, "a.selected.level");
+
+			// #MenuId a.static.selected { ...;text-decoration:none; }
+			RenderAnchorStyle (block, owner.StaticSelectedStyleInternal, "a.static.selected");
+			
+			// #MenuId a.dynamic.selected { ...;text-decoration:none;border-style:none; }
+			RenderAnchorStyle (block, owner.DynamicSelectedStyleInternal, "a.dynamic.selected");
+
+			// #MenuId a.static.highlighted { ... }
+			style = owner.StaticHoverStyleInternal;
+			if (style != null)
+				block.RegisterStyle (style, "a.static.highlighted");
+			
+			// #MenuId a.dynamic.highlighted { ... }
+			style = owner.DynamicHoverStyleInternal;
+			if (style != null)
+				block.RegisterStyle (style, "a.dynamic.highlighted");
+			
+			head.Controls.Add (block);
+		}
+		
+		public override void RenderBeginTag (HtmlTextWriter writer, string skipLinkText)
+		{
+			Menu owner = Owner;
+			
+			// <a href="#ID_SkipLink">
+			writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + owner.ClientID + "_SkipLink");
+			writer.RenderBeginTag (HtmlTextWriterTag.A);
+				
+			// <img alt="" height="0" width="0" src="" style="border-width:0px;"/>
+			writer.AddAttribute (HtmlTextWriterAttribute.Alt, skipLinkText);
+			Page page = owner.Page;
+			ClientScriptManager csm = page != null ? page.ClientScript : new ClientScriptManager (null);
+				
+			writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
+			writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
+			writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
+			
+			writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
+			writer.RenderBeginTag (HtmlTextWriterTag.Img);
+			writer.RenderEndTag ();
+				
+			writer.RenderEndTag (); // </a>
+		}
+		
+		public override void RenderEndTag (HtmlTextWriter writer)
+		{
+		}
+		
+		public override void AddAttributesToRender (HtmlTextWriter writer)
+		{
+			// do nothing
+		}
+
+		public override void RenderContents (HtmlTextWriter writer)
+		{
+			Menu owner = Owner;
+			MenuItemCollection items = owner.Items;
+			owner.RenderMenu (writer, items, owner.Orientation == Orientation.Vertical, false, 0, items.Count > 1);
+		}
+		
+		public override void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
+		{
+			if (dynamic || menuLevel == 0) {
+				var style = new SubMenuStyle ();
+				AddCssClass (style, "level" + (menuLevel + 1));
+				FillMenuStyle (null, dynamic, menuLevel, style);
+				style.AddAttributesToRender (writer);
+				writer.RenderBeginTag (HtmlTextWriterTag.Ul);
+			}
+		}
+
+		public override void RenderMenuEndTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
+		{
+			if (dynamic || menuLevel == 0)
+				base.RenderMenuEndTag (writer, dynamic, menuLevel);
+		}
+		
+		public override void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast)
+		{
+			Menu owner = Owner;
+			int count = items.Count;
+			var oc = new OwnerContext (this);
+			
+			for (int n = 0; n < count; n++) {
+				MenuItem item = items [n];
+				Adapters.MenuAdapter adapter = owner.Adapter as Adapters.MenuAdapter;
+				if (adapter != null)
+					adapter.RenderItem (writer, item, n);
+				else
+				 	RenderMenuItem (writer, item, vertical, (n + 1) == count ? notLast : true, n == 0, oc);
+			}
+		}
+
+		protected override void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc)
+		{
+			Menu owner = Owner;
+			string clientID = oc.ClientID;
+			bool displayChildren = owner.DisplayChildren (item);
+			bool isDynamicItem = IsDynamicItem (owner, item);
+			int itemLevel = item.Depth + 1;
+			string str;
+			
+			writer.RenderBeginTag (HtmlTextWriterTag.Li);
+
+			if (isDynamicItem)
+				RenderSeparatorImage (owner, writer, oc.DynamicTopSeparatorImageUrl, true);
+			else
+				RenderSeparatorImage (owner, writer, oc.StaticTopSeparatorImageUrl, true);
+			
+			var linkStyle = new Style ();
+			if (displayChildren && (isDynamicItem || itemLevel >= oc.StaticDisplayLevels))
+				AddCssClass (linkStyle, isDynamicItem && haveDynamicPopOut ? "popout-dynamic" : "popout");
+			AddCssClass (linkStyle, "level" + itemLevel);
+
+			MenuItemStyleCollection levelStyles = oc.LevelMenuItemStyles;
+			if (levelStyles != null && levelStyles.Count >= itemLevel) {
+				MenuItemStyle style = levelStyles [itemLevel - 1];
+				string cssClass = style.CssClass;
+				if (!String.IsNullOrEmpty (cssClass))
+					AddCssClass (linkStyle, cssClass);
+			}
+
+			if (owner.SelectedItem == item)
+				AddCssClass (linkStyle, "selected");
+			
+			str = item.ToolTip;
+			if (!String.IsNullOrEmpty (str))
+				writer.AddAttribute ("title", str);
+			linkStyle.AddAttributesToRender (writer);
+			RenderItemHref (owner, writer, item);
+			writer.RenderBeginTag (HtmlTextWriterTag.A);
+			owner.RenderItemContent (writer, item, isDynamicItem);
+			writer.RenderEndTag ();
+
+			str = item.SeparatorImageUrl;
+			if (String.IsNullOrEmpty (str)) {
+				if (isDynamicItem)
+					str = oc.DynamicBottomSeparatorImageUrl;
+				else
+					str = oc.StaticBottomSeparatorImageUrl;
+			}
+			RenderSeparatorImage (owner, writer, str, true);
+
+			// if (itemLevel == 1)
+			// 	writer.RenderEndTag (); // </li>
+			
+			if (displayChildren)
+				owner.RenderMenu (writer, item.ChildItems, vertical, isDynamicItem, itemLevel, notLast);
+
+			if (itemLevel > 1)
+				writer.RenderEndTag (); // </li>
+		}
+
+		public override bool IsDynamicItem (Menu owner, MenuItem item)
+		{
+			if (owner == null)
+				throw new ArgumentNullException ("owner");
+
+			if (item == null)
+				throw new ArgumentNullException ("item");
+			
+			return item.Depth + 1 >= Owner.StaticDisplayLevels;
+		}
+		
+		NamedCssStyleCollection RenderAnchorStyle (StyleBlock block, Style style, string styleName)
+		{
+			if (style == null || block == null)
+				return null;
+
+			style.AlwaysRenderTextDecoration = true;
+			NamedCssStyleCollection css = block.RegisterStyle (style, styleName);
+			if (style.BorderStyle == BorderStyle.NotSet)
+				css.Add (HtmlTextWriterStyle.BorderStyle, "none");
+
+			return css;
+		}
+
+		void RenderLevelStyles (StyleBlock block, int num, IList levelStyles, string name, string unitName = null, double indent = 0)
+		{
+			int stylesCount = levelStyles != null ? levelStyles.Count : 0;
+			bool haveStyles = stylesCount > 0;
+			if (!haveStyles || block == null)
+				return;
+
+			NamedCssStyleCollection css;
+			Style style;
+			bool haveIndent = !String.IsNullOrEmpty (unitName) && indent != 0;
+			for (int i = 0; i < stylesCount; i++) {
+				if ((i == 0 && !haveStyles))
+					continue;
+				
+				css = block.RegisterStyle (name + (i + 1));
+				if (haveStyles && stylesCount > i) {
+					style = levelStyles [i] as Style;
+					if (style != null) {
+						style.AlwaysRenderTextDecoration = true;
+						css.CopyFrom (style.GetStyleAttributes (null));
+					}
+				}
+				
+				if (haveIndent && i > 0 && i < num) {
+					css.Add (HtmlTextWriterStyle.PaddingLeft, indent.ToString (CultureInfo.InvariantCulture) + unitName);
+					indent += indent;
+				}
+			}
+		}
+	}
+}

+ 517 - 0
mcs/class/System.Web/System.Web.UI.WebControls/MenuTableRenderer.cs

@@ -0,0 +1,517 @@
+//
+// Authors:
+//	Lluis Sanchez Gual ([email protected])
+//	Igor Zelmanovich ([email protected])
+//	Marek Habersack <[email protected]>
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Web;
+using System.Web.UI;
+using System.Web.UI.HtmlControls;
+
+namespace System.Web.UI.WebControls
+{
+	sealed class MenuTableRenderer : BaseMenuRenderer
+	{
+		const string onPreRenderScript = "var {0} = new Object ();\n{0}.webForm = {1};\n{0}.disappearAfter = {2};\n{0}.vertical = {3};";
+
+		public override HtmlTextWriterTag Tag {
+			get { return HtmlTextWriterTag.Table; }
+			
+		}
+		
+		public MenuTableRenderer (Menu owner)
+			: base (owner)
+		{
+		}
+		
+		public override void AddAttributesToRender (HtmlTextWriter writer)
+		{
+			writer.AddAttribute ("cellpadding", "0", false);
+			writer.AddAttribute ("cellspacing", "0", false);
+			writer.AddAttribute ("border", "0", false);
+
+			base.AddAttributesToRender (writer);
+		}
+		
+		public override void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script)
+		{
+			Menu owner = Owner;
+			MenuItemStyle staticMenuItemStyle = owner.StaticMenuItemStyleInternal;
+			SubMenuStyle staticMenuStyle = owner.StaticMenuStyleInternal;
+			MenuItemStyle dynamicMenuItemStyle = owner.DynamicMenuItemStyleInternal;
+			SubMenuStyle dynamicMenuStyle = owner.DynamicMenuStyleInternal;
+			MenuItemStyleCollection levelMenuItemStyles = owner.LevelMenuItemStyles;
+			List<Style> levelMenuItemLinkStyles = owner.LevelMenuItemLinkStyles;
+			SubMenuStyleCollection levelSubMenuStyles = owner.LevelSubMenuStylesInternal;
+			MenuItemStyle staticSelectedStyle = owner.StaticSelectedStyleInternal;
+			MenuItemStyle dynamicSelectedStyle = owner.DynamicSelectedStyleInternal;
+			MenuItemStyleCollection levelSelectedStyles = owner.LevelSelectedStylesInternal;
+			List<Style> levelSelectedLinkStyles = owner.LevelSelectedLinkStyles;
+			Style staticHoverStyle = owner.StaticHoverStyleInternal;
+			Style dynamicHoverStyle = owner.DynamicHoverStyleInternal;
+			
+			if (!csm.IsClientScriptIncludeRegistered (typeof (Menu), "Menu.js")) {
+				string url = csm.GetWebResourceUrl (typeof (Menu), "Menu.js");
+				csm.RegisterClientScriptInclude (typeof (Menu), "Menu.js", url);
+			}
+			
+			script.AppendFormat (onPreRenderScript,
+					     cmenu,
+					     page.IsMultiForm ? page.theForm : "window",
+					     ClientScriptManager.GetScriptLiteral (owner.DisappearAfter),
+					     ClientScriptManager.GetScriptLiteral (owner.Orientation == Orientation.Vertical));
+
+			if (owner.DynamicHorizontalOffset != 0)
+				script.Append (String.Concat (cmenu, ".dho = ", ClientScriptManager.GetScriptLiteral (owner.DynamicHorizontalOffset), ";\n"));
+			if (owner.DynamicVerticalOffset != 0)
+				script.Append (String.Concat (cmenu, ".dvo = ", ClientScriptManager.GetScriptLiteral (owner.DynamicVerticalOffset), ";\n"));
+
+			// The order in which styles are defined matters when more than one class
+			// is assigned to an element
+			RegisterStyle (owner.PopOutBoxStyle, head);
+			RegisterStyle (owner.ControlStyle, owner.ControlLinkStyle, head);
+			
+			if (staticMenuItemStyle != null)
+				RegisterStyle (owner.StaticMenuItemStyle, owner.StaticMenuItemLinkStyle, head);
+
+			if (staticMenuStyle != null)
+				RegisterStyle (owner.StaticMenuStyle, head);
+			
+			if (dynamicMenuItemStyle != null)
+				RegisterStyle (owner.DynamicMenuItemStyle, owner.DynamicMenuItemLinkStyle, head);
+
+			if (dynamicMenuStyle != null)
+				RegisterStyle (owner.DynamicMenuStyle, head);
+
+			if (levelMenuItemStyles != null && levelMenuItemStyles.Count > 0) {
+				levelMenuItemLinkStyles = new List<Style> (levelMenuItemStyles.Count);
+				foreach (Style style in levelMenuItemStyles) {
+					Style linkStyle = new Style ();
+					levelMenuItemLinkStyles.Add (linkStyle);
+					RegisterStyle (style, linkStyle, head);
+				}
+			}
+		
+			if (levelSubMenuStyles != null)
+				foreach (Style style in levelSubMenuStyles)
+					RegisterStyle (style, head);
+
+			if (staticSelectedStyle != null)
+				RegisterStyle (staticSelectedStyle, owner.StaticSelectedLinkStyle, head);
+			
+			if (dynamicSelectedStyle != null)
+				RegisterStyle (dynamicSelectedStyle, owner.DynamicSelectedLinkStyle, head);
+
+			if (levelSelectedStyles != null && levelSelectedStyles.Count > 0) {
+				levelSelectedLinkStyles = new List<Style> (levelSelectedStyles.Count);
+				foreach (Style style in levelSelectedStyles) {
+					Style linkStyle = new Style ();
+					levelSelectedLinkStyles.Add (linkStyle);
+					RegisterStyle (style, linkStyle, head);
+				}
+			}
+			
+			if (staticHoverStyle != null) {
+				if (head == null)
+					throw new InvalidOperationException ("Using Menu.StaticHoverStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
+				RegisterStyle (staticHoverStyle, owner.StaticHoverLinkStyle, head);
+				script.Append (String.Concat (cmenu, ".staticHover = ", ClientScriptManager.GetScriptLiteral (staticHoverStyle.RegisteredCssClass), ";\n"));
+				script.Append (String.Concat (cmenu, ".staticLinkHover = ", ClientScriptManager.GetScriptLiteral (owner.StaticHoverLinkStyle.RegisteredCssClass), ";\n"));
+			}
+			
+			if (dynamicHoverStyle != null) {
+				if (head == null)
+					throw new InvalidOperationException ("Using Menu.DynamicHoverStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
+				RegisterStyle (dynamicHoverStyle, owner.DynamicHoverLinkStyle, head);
+				script.Append (String.Concat (cmenu, ".dynamicHover = ", ClientScriptManager.GetScriptLiteral (dynamicHoverStyle.RegisteredCssClass), ";\n"));
+				script.Append (String.Concat (cmenu, ".dynamicLinkHover = ", ClientScriptManager.GetScriptLiteral (owner.DynamicHoverLinkStyle.RegisteredCssClass), ";\n"));
+			}
+		}
+
+		public override void RenderBeginTag (HtmlTextWriter writer, string skipLinkText)
+		{
+			Menu owner = Owner;
+			
+			// <a href="#ID_SkipLink">
+			writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + owner.ClientID + "_SkipLink");
+			writer.RenderBeginTag (HtmlTextWriterTag.A);
+				
+			// <img alt="" height="0" width="0" src="" style="border-width:0px;"/>
+			writer.AddAttribute (HtmlTextWriterAttribute.Alt, skipLinkText);
+			writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
+			writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
+				
+			Page page = owner.Page;
+			ClientScriptManager csm = page != null ? page.ClientScript : new ClientScriptManager (null);
+				
+			writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
+			writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
+			writer.RenderBeginTag (HtmlTextWriterTag.Img);
+			writer.RenderEndTag ();
+				
+			writer.RenderEndTag (); // </a>
+		}
+		
+		public override void RenderEndTag (HtmlTextWriter writer)
+		{
+			Menu owner = Owner;
+			if (owner.StaticDisplayLevels == 1 && owner.MaximumDynamicDisplayLevels > 0)
+				owner.RenderDynamicMenu (writer, owner.Items);
+		}
+
+		public override void RenderContents (HtmlTextWriter writer)
+		{
+			Menu owner = Owner;
+			RenderMenuBody (writer, owner.Items, owner.Orientation == Orientation.Vertical, false, false);
+		}
+		
+		void RenderMenuBeginTagAttributes (HtmlTextWriter writer, bool dynamic, int menuLevel)
+		{
+			writer.AddAttribute ("cellpadding", "0", false);
+			writer.AddAttribute ("cellspacing", "0", false);
+			writer.AddAttribute ("border", "0", false);
+
+			if (!dynamic) {
+				SubMenuStyle style = new SubMenuStyle ();
+				FillMenuStyle (null, dynamic, menuLevel, style);
+				style.AddAttributesToRender (writer);
+			}
+		}
+		
+		public override void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
+		{
+			RenderMenuBeginTagAttributes (writer, dynamic, menuLevel);
+			writer.RenderBeginTag (HtmlTextWriterTag.Table);
+		}
+
+		void RenderMenuItemSpacing (HtmlTextWriter writer, Unit itemSpacing, bool vertical)
+		{
+			if (vertical) {
+				writer.AddStyleAttribute ("height", itemSpacing.ToString ());
+				writer.RenderBeginTag (HtmlTextWriterTag.Tr);
+				writer.RenderBeginTag (HtmlTextWriterTag.Td);
+				writer.RenderEndTag ();
+				writer.RenderEndTag ();
+			} else {
+				writer.AddStyleAttribute ("width", itemSpacing.ToString ());
+				writer.RenderBeginTag (HtmlTextWriterTag.Td);
+				writer.RenderEndTag ();
+			}
+		}
+		
+		public override void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast)
+		{
+			Menu owner = Owner;
+			if (!vertical)
+				writer.RenderBeginTag (HtmlTextWriterTag.Tr);
+
+			int count = items.Count;
+			var oc = new OwnerContext (this);
+			
+			for (int n = 0; n < count; n++) {
+				MenuItem item = items [n];
+				Adapters.MenuAdapter adapter = owner.Adapter as Adapters.MenuAdapter;
+				if (adapter != null)
+					adapter.RenderItem (writer, item, n);
+				else
+					RenderMenuItem (writer, item, vertical, (n + 1) == count ? notLast : true, n == 0, oc);
+			}
+
+			if (!vertical)
+				writer.RenderEndTag ();	// TR
+		}
+
+		protected override void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc)
+		{
+			Menu owner = Owner;
+			string clientID = oc.ClientID;
+			bool displayChildren = owner.DisplayChildren (item);
+			bool dynamicChildren = displayChildren && (item.Depth + 1 >= oc.StaticDisplayLevels);
+			bool isDynamicItem = IsDynamicItem (owner, item);
+			bool isVertical = oc.IsVertical || isDynamicItem;
+			Unit itemSpacing = owner.GetItemSpacing (item, isDynamicItem);
+
+			if (itemSpacing != Unit.Empty && (item.Depth > 0 || !isFirst))
+				RenderMenuItemSpacing (writer, itemSpacing, isVertical);
+
+			if(!String.IsNullOrEmpty(item.ToolTip))
+				writer.AddAttribute (HtmlTextWriterAttribute.Title, item.ToolTip);
+			if (isVertical)
+				writer.RenderBeginTag (HtmlTextWriterTag.Tr);
+
+			string parentId = isDynamicItem ? "'" + item.Parent.Path + "'" : "null";
+			if (dynamicChildren) {
+				writer.AddAttribute ("onmouseover",
+						     "javascript:Menu_OverItem ('" + clientID + "','" + item.Path + "'," + parentId + ")");
+				writer.AddAttribute ("onmouseout",
+						     "javascript:Menu_OutItem ('" + clientID + "','" + item.Path + "')");
+			} else if (isDynamicItem) {
+				writer.AddAttribute ("onmouseover",
+						     "javascript:Menu_OverDynamicLeafItem ('" + clientID + "','" + item.Path + "'," + parentId + ")");
+				writer.AddAttribute ("onmouseout",
+						     "javascript:Menu_OutItem ('" + clientID + "','" + item.Path + "'," + parentId + ")");
+			} else {
+				writer.AddAttribute ("onmouseover",
+						     "javascript:Menu_OverStaticLeafItem ('" + clientID + "','" + item.Path + "')");
+				writer.AddAttribute ("onmouseout",
+						     "javascript:Menu_OutItem ('" + clientID + "','" + item.Path + "')");
+			}
+
+			writer.RenderBeginTag (HtmlTextWriterTag.Td);
+
+			// Top separator image
+
+			if (isDynamicItem)
+				RenderSeparatorImage (owner, writer, oc.DynamicTopSeparatorImageUrl, false);
+			else
+				RenderSeparatorImage (owner, writer, oc.StaticTopSeparatorImageUrl, false);
+
+			// Menu item box
+			
+			MenuItemStyle style = new MenuItemStyle ();
+				
+			if (oc.Header != null) {
+				// styles are registered
+				if (!isDynamicItem && oc.StaticMenuItemStyle != null) {
+					AddCssClass (style, oc.StaticMenuItemStyle.CssClass);
+					AddCssClass (style, oc.StaticMenuItemStyle.RegisteredCssClass);
+				}
+				if (isDynamicItem && oc.DynamicMenuItemStyle != null) {
+					AddCssClass (style, oc.DynamicMenuItemStyle.CssClass);
+					AddCssClass (style, oc.DynamicMenuItemStyle.RegisteredCssClass);
+				}
+				if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth) {
+					AddCssClass (style, oc.LevelMenuItemStyles [item.Depth].CssClass);
+					AddCssClass (style, oc.LevelMenuItemStyles [item.Depth].RegisteredCssClass);
+				}
+				if (item == oc.SelectedItem) {
+					if (!isDynamicItem && oc.StaticSelectedStyle != null) {
+						AddCssClass (style, oc.StaticSelectedStyle.CssClass);
+						AddCssClass (style, oc.StaticSelectedStyle.RegisteredCssClass);
+					}
+					if (isDynamicItem && oc.DynamicSelectedStyle != null) {
+						AddCssClass (style, oc.DynamicSelectedStyle.CssClass);
+						AddCssClass (style, oc.DynamicSelectedStyle.RegisteredCssClass);
+					}
+					if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth) {
+						AddCssClass (style, oc.LevelSelectedStyles [item.Depth].CssClass);
+						AddCssClass (style, oc.LevelSelectedStyles [item.Depth].RegisteredCssClass);
+					}
+				}
+			} else {
+				// styles are not registered
+				if (!isDynamicItem && oc.StaticMenuItemStyle != null)
+					style.CopyFrom (oc.StaticMenuItemStyle);
+				if (isDynamicItem && oc.DynamicMenuItemStyle != null)
+					style.CopyFrom (oc.DynamicMenuItemStyle);
+				if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth)
+					style.CopyFrom (oc.LevelMenuItemStyles [item.Depth]);
+				if (item == oc.SelectedItem) {
+					if (!isDynamicItem && oc.StaticSelectedStyle != null)
+						style.CopyFrom (oc.StaticSelectedStyle);
+					if (isDynamicItem && oc.DynamicSelectedStyle != null)
+						style.CopyFrom (oc.DynamicSelectedStyle);
+					if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth)
+						style.CopyFrom (oc.LevelSelectedStyles [item.Depth]);
+				}
+			}
+			style.AddAttributesToRender (writer);
+
+			writer.AddAttribute ("id", GetItemClientId (clientID, item, "i"));
+			writer.AddAttribute ("cellpadding", "0", false);
+			writer.AddAttribute ("cellspacing", "0", false);
+			writer.AddAttribute ("border", "0", false);
+			writer.AddAttribute ("width", "100%", false);
+			writer.RenderBeginTag (HtmlTextWriterTag.Table);
+			writer.RenderBeginTag (HtmlTextWriterTag.Tr);
+
+			// Menu item text
+
+			if (isVertical)
+				writer.AddStyleAttribute (HtmlTextWriterStyle.Width, "100%");
+			if (!owner.ItemWrap)
+				writer.AddStyleAttribute ("white-space", "nowrap");
+			writer.RenderBeginTag (HtmlTextWriterTag.Td);
+
+			RenderItemHref (owner, writer, item);
+			
+			Style linkStyle = new Style ();
+			if (oc.Header != null) {
+				// styles are registered
+				AddCssClass (linkStyle, oc.ControlLinkStyle.RegisteredCssClass);
+
+				if (!isDynamicItem && oc.StaticMenuItemStyle != null) {
+					AddCssClass (linkStyle, oc.StaticMenuItemStyle.CssClass);
+					AddCssClass (linkStyle, oc.StaticMenuItemLinkStyle.RegisteredCssClass);
+				}
+				if (isDynamicItem && oc.DynamicMenuItemStyle != null) {
+					AddCssClass (linkStyle, oc.DynamicMenuItemStyle.CssClass);
+					AddCssClass (linkStyle, oc.DynamicMenuItemLinkStyle.RegisteredCssClass);
+				}
+				if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth) {
+					AddCssClass (linkStyle, oc.LevelMenuItemStyles [item.Depth].CssClass);
+					AddCssClass (linkStyle, oc.LevelMenuItemLinkStyles [item.Depth].RegisteredCssClass);
+				}
+				if (item == oc.SelectedItem) {
+					if (!isDynamicItem && oc.StaticSelectedStyle != null) {
+						AddCssClass (linkStyle, oc.StaticSelectedStyle.CssClass);
+						AddCssClass (linkStyle, oc.StaticSelectedLinkStyle.RegisteredCssClass);
+					}
+					if (isDynamicItem && oc.DynamicSelectedStyle != null) {
+						AddCssClass (linkStyle, oc.DynamicSelectedStyle.CssClass);
+						AddCssClass (linkStyle, oc.DynamicSelectedLinkStyle.RegisteredCssClass);
+					}
+					if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth) {
+						AddCssClass (linkStyle, oc.LevelSelectedStyles [item.Depth].CssClass);
+						AddCssClass (linkStyle, oc.LevelSelectedLinkStyles [item.Depth].RegisteredCssClass);
+					}
+				}
+			} else {
+				// styles are not registered
+				linkStyle.CopyFrom (oc.ControlLinkStyle);
+
+				if (!isDynamicItem && oc.StaticMenuItemStyle != null)
+					linkStyle.CopyFrom (oc.StaticMenuItemLinkStyle);
+				if (isDynamicItem && oc.DynamicMenuItemStyle != null)
+					linkStyle.CopyFrom (oc.DynamicMenuItemLinkStyle);
+				if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth)
+					linkStyle.CopyFrom (oc.LevelMenuItemLinkStyles [item.Depth]);
+				if (item == oc.SelectedItem) {
+					if (!isDynamicItem && oc.StaticSelectedStyle != null)
+						linkStyle.CopyFrom (oc.StaticSelectedLinkStyle);
+					if (isDynamicItem && oc.DynamicSelectedStyle != null)
+						linkStyle.CopyFrom (oc.DynamicSelectedLinkStyle);
+					if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth)
+						linkStyle.CopyFrom (oc.LevelSelectedLinkStyles [item.Depth]);
+				}
+
+				linkStyle.AlwaysRenderTextDecoration = true;
+			}
+			linkStyle.AddAttributesToRender (writer);
+
+			writer.AddAttribute ("id", GetItemClientId (clientID, item, "l"));
+			
+			if (item.Depth > 0 && !isDynamicItem) {
+				double value;
+#if NET_4_0
+				Unit unit = oc.StaticSubMenuIndent;
+				if (unit == Unit.Empty)
+					value = 16;
+				else
+					value = unit.Value;
+#else
+				value = oc.StaticSubMenuIndent.Value;
+#endif
+				Unit indent = new Unit (value * item.Depth, oc.StaticSubMenuIndent.Type);
+				writer.AddStyleAttribute (HtmlTextWriterStyle.MarginLeft, indent.ToString ());
+			}
+			writer.RenderBeginTag (HtmlTextWriterTag.A);
+			owner.RenderItemContent (writer, item, isDynamicItem);
+			writer.RenderEndTag ();	// A
+
+			writer.RenderEndTag ();	// TD
+
+			// Popup image
+
+			if (dynamicChildren) {
+				string popOutImage = GetPopOutImage (owner, item, isDynamicItem);
+				if (popOutImage != null) {
+					writer.RenderBeginTag (HtmlTextWriterTag.Td);
+					writer.AddAttribute ("src", owner.ResolveClientUrl (popOutImage));
+					writer.AddAttribute ("border", "0");
+					string toolTip = String.Format (isDynamicItem ? oc.DynamicPopOutImageTextFormatString : oc.StaticPopOutImageTextFormatString, item.Text);
+					writer.AddAttribute (HtmlTextWriterAttribute.Alt, toolTip);
+					writer.RenderBeginTag (HtmlTextWriterTag.Img);
+					writer.RenderEndTag ();	// IMG
+					writer.RenderEndTag ();	// TD
+				}
+			}
+
+			writer.RenderEndTag ();	// TR
+			writer.RenderEndTag ();	// TABLE
+			
+			writer.RenderEndTag ();	// TD
+
+			if (!isVertical && itemSpacing == Unit.Empty && (notLast || (displayChildren && !dynamicChildren))) {
+				writer.AddStyleAttribute ("width", "3px");
+				writer.RenderBeginTag (HtmlTextWriterTag.Td);
+				writer.RenderEndTag ();
+			}
+			
+			// Bottom separator image
+			string separatorImg = item.SeparatorImageUrl;
+			if (separatorImg.Length == 0) {
+				if (isDynamicItem)
+					separatorImg = oc.DynamicBottomSeparatorImageUrl;
+				else
+					separatorImg = oc.StaticBottomSeparatorImageUrl;
+			}
+			
+			if (separatorImg.Length > 0) {
+				if (!isVertical)
+					writer.RenderBeginTag (HtmlTextWriterTag.Td);
+				RenderSeparatorImage (owner, writer, separatorImg, false);
+				if (!isVertical)
+					writer.RenderEndTag (); // TD
+			}
+
+			if (isVertical)
+				writer.RenderEndTag ();	// TR
+
+			if (itemSpacing != Unit.Empty)
+				RenderMenuItemSpacing (writer, itemSpacing, isVertical);
+
+			// Submenu
+
+			if (displayChildren && !dynamicChildren) {
+				if (isVertical)
+					writer.RenderBeginTag (HtmlTextWriterTag.Tr);
+				writer.RenderBeginTag (HtmlTextWriterTag.Td);
+				writer.AddAttribute ("width", "100%");
+				owner.RenderMenu (writer, item.ChildItems, vertical, false, item.Depth + 1, notLast);
+				if (item.Depth + 2 == oc.StaticDisplayLevels)
+					owner.RenderDynamicMenu (writer, item.ChildItems);
+				writer.RenderEndTag ();	// TD
+				if (isVertical)
+					writer.RenderEndTag ();	// TR
+			}
+		}
+
+		public override bool IsDynamicItem (Menu owner, MenuItem item)
+		{
+			if (owner == null)
+				throw new ArgumentNullException ("owner");
+
+			if (item == null)
+				throw new ArgumentNullException ("item");
+			
+			return item.Depth + 1 > owner.StaticDisplayLevels;
+		}
+	}
+}

+ 89 - 0
mcs/class/System.Web/System.Web.UI.WebControls/NamedCssStyleCollection.cs

@@ -0,0 +1,89 @@
+//
+// Authors:
+//	Marek Habersack <[email protected]>
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Web.UI;
+
+namespace System.Web.UI.WebControls
+{
+	sealed class NamedCssStyleCollection
+	{
+		CssStyleCollection collection;
+			
+		public CssStyleCollection Collection {
+			get {
+				if (collection == null)
+					collection = new CssStyleCollection ();
+
+				return collection;
+			}
+		}
+			
+		public string Name { get; private set; }
+			
+		public NamedCssStyleCollection (string name)
+		{
+			if (name == null)
+				name = String.Empty;
+
+			Name = name;
+		}
+
+		public NamedCssStyleCollection CopyFrom (CssStyleCollection coll)
+		{
+			if (coll == null)
+				return this;
+
+			CssStyleCollection collection = Collection;
+			foreach (string key in coll.Keys)
+				collection.Add (key, coll [key]);
+
+			return this;
+		}
+
+		public NamedCssStyleCollection Add (HtmlTextWriterStyle key, string value)
+		{
+			Collection.Add (key, value);
+			return this;
+		}
+
+		public NamedCssStyleCollection Add (string key, string value)
+		{
+			Collection.Add (key, value);
+			return this;
+		}
+
+		public NamedCssStyleCollection Add (Style style)
+		{
+			if (style != null)
+				CopyFrom (style.GetStyleAttributes (null));
+
+			return this;
+		}
+	}
+}

+ 135 - 0
mcs/class/System.Web/System.Web.UI.WebControls/StyleBlock.cs

@@ -0,0 +1,135 @@
+//
+// Authors:
+//	Marek Habersack <[email protected]>
+//
+// (C) 2010 Novell, Inc (http://novell.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Web.UI;
+
+namespace System.Web.UI.WebControls
+{
+	sealed class StyleBlock : Control
+	{
+		List <NamedCssStyleCollection> cssStyles;
+		Dictionary <string, NamedCssStyleCollection> cssStyleIndex;
+		string stylePrefix;
+		
+		List <NamedCssStyleCollection> CssStyles {
+			get {
+				if (cssStyles == null) {
+					cssStyles = new List <NamedCssStyleCollection> ();
+					cssStyleIndex = new Dictionary <string, NamedCssStyleCollection> (StringComparer.Ordinal);
+				}
+				
+				return cssStyles;
+			}
+		}
+		
+		public StyleBlock (string stylePrefix)
+		{
+			if (String.IsNullOrEmpty (stylePrefix))
+				throw new ArgumentNullException ("stylePrefix");
+
+			this.stylePrefix = stylePrefix;
+		}
+
+		public NamedCssStyleCollection RegisterStyle (string name = null)
+		{
+			if (name == null)
+				name = String.Empty;
+
+			return GetStyle (name);
+		}
+		
+		public NamedCssStyleCollection RegisterStyle (Style style, string name = null)
+		{
+			if (style == null)
+				throw new ArgumentNullException ("style");
+			
+			if (name == null)
+				name = String.Empty;
+
+			NamedCssStyleCollection cssStyle = GetStyle (name);
+			cssStyle.CopyFrom (style.GetStyleAttributes (null));
+
+			return cssStyle;
+		}
+
+		public NamedCssStyleCollection RegisterStyle (HtmlTextWriterStyle key, string value, string styleName = null)
+		{
+			if (styleName == null)
+				styleName = String.Empty;
+
+			NamedCssStyleCollection style = GetStyle (styleName);
+			style.Add (key, value);
+
+			return style;
+		}
+		
+		NamedCssStyleCollection GetStyle (string name)
+		{
+			List <NamedCssStyleCollection> cssStyles = CssStyles;
+			NamedCssStyleCollection style;
+
+			if (!cssStyleIndex.TryGetValue (name, out style)) {
+				style = new NamedCssStyleCollection (name);
+				cssStyleIndex.Add (name, style);
+				cssStyles.Add (style);
+			}
+
+			if (style == null)
+				throw new InvalidOperationException (String.Format ("Internal error. Stylesheet for style {0} is null.", name));
+			
+			return style;
+		}
+		
+		protected internal override void Render (HtmlTextWriter writer)
+		{
+			if (cssStyles == null || cssStyles.Count == 0)
+				return;
+
+			writer.AddAttribute (HtmlTextWriterAttribute.Type, "text/css");
+			writer.RenderBeginTag (HtmlTextWriterTag.Style);
+			writer.WriteLine ("/* <![CDATA[ */");
+
+			string name, value;
+			foreach (var css in cssStyles) {
+				value = css.Collection.Value;
+				if (String.IsNullOrEmpty (value))
+					continue;
+				
+				name = css.Name;
+				if (name != String.Empty)
+					name = name + " ";
+				
+				writer.WriteLine ("#{0} {1}{{ {2} }}", stylePrefix, name, value);
+			}
+			
+			writer.WriteLine ("/* ]]> */");
+			writer.RenderEndTag (); // </style>
+		}
+	}
+}

+ 1 - 1
mcs/class/System.Web/System.Web.UI.WebControls/TreeView.cs

@@ -1621,7 +1621,7 @@ namespace System.Web.UI.WebControls
 				return;
 
 			writer.RenderBeginTag (HtmlTextWriterTag.Table);
-			writer.AddAttribute (HtmlTextWriterAttribute.Height, ((int) value).ToString () + "dupa1", false);
+			writer.AddAttribute (HtmlTextWriterAttribute.Height, ((int) value).ToString (), false);
 			writer.RenderBeginTag (HtmlTextWriterTag.Tr);
 			writer.RenderBeginTag (HtmlTextWriterTag.Td);
 			writer.RenderEndTag (); // td

+ 3 - 0
mcs/class/System.Web/System.Web.dll.sources

@@ -722,6 +722,7 @@ System.Web.UI.WebControls/AutoGeneratedFieldProperties.cs
 System.Web.UI.WebControls/BaseCompareValidator.cs
 System.Web.UI.WebControls/BaseDataBoundControl.cs
 System.Web.UI.WebControls/BaseDataList.cs
+System.Web.UI.WebControls/BaseMenuRenderer.cs
 System.Web.UI.WebControls/BaseValidator.cs
 System.Web.UI.WebControls/BorderStyle.cs
 System.Web.UI.WebControls/BoundColumn.cs
@@ -912,6 +913,7 @@ System.Web.UI.WebControls/ImageField.cs
 System.Web.UI.WebControls/ImageMap.cs
 System.Web.UI.WebControls/ImageMapEventArgs.cs
 System.Web.UI.WebControls/ImageMapEventHandler.cs
+System.Web.UI.WebControls/IMenuRenderer.cs
 System.Web.UI.WebControls/IPersistedSelector.cs
 System.Web.UI.WebControls/IPostBackContainer.cs
 System.Web.UI.WebControls/IRepeatInfoUser.cs
@@ -952,6 +954,7 @@ System.Web.UI.WebControls/MenuItem.cs
 System.Web.UI.WebControls/MenuItemStyleCollection.cs
 System.Web.UI.WebControls/MenuItemStyle.cs
 System.Web.UI.WebControls/MenuItemTemplateContainer.cs
+System.Web.UI.WebControls/MenuTableRenderer.cs
 System.Web.UI.WebControls/MonthChangedEventArgs.cs
 System.Web.UI.WebControls/MonthChangedEventHandler.cs
 System.Web.UI.WebControls/MultiView.cs

+ 3 - 0
mcs/class/System.Web/Test/System.Web.UI.WebControls.Adapters/MenuAdapterTest.cs

@@ -58,6 +58,9 @@ namespace MonoTests.System.Web.UI.WebControls.Adapters
 		{
 			p = new Page ();
 			c = new MyMenu ();
+#if NET_4_0
+			c.RenderingMode = MenuRenderingMode.Table;
+#endif
 			a = new MyMenuAdapter (c);
 			p.Controls.Add(c);
 			sw = new StringWriter ();

File diff suppressed because it is too large
+ 68 - 9
mcs/class/System.Web/Test/System.Web.UI.WebControls/MenuTest.cs


+ 1 - 1
mcs/class/System.Web/Test/standalone-tests/Menu_4.0_List.cs

@@ -38,7 +38,7 @@ using NUnit.Framework;
 
 namespace StandAloneTests.Menu_40_List
 {
-	[TestCase ("Menu_40_List", "Menu control List rendering mode for 4.0")]
+	[TestCase ("Menu_40_List", "Menu control List rendering mode for 4.0", Disabled=true)]
 	public sealed class Test_01 : ITestCase
 	{
 		public string PhysicalPath {

+ 3 - 0
mcs/class/System.Web/net_4_0_System.Web.dll.sources

@@ -28,8 +28,11 @@ System.Web.UI.WebControls/IDataBoundItemControl.cs
 System.Web.UI.WebControls/IDataBoundListControl.cs
 System.Web.UI.WebControls/IFieldControl.cs
 System.Web.UI.WebControls/IRenderOuterTable.cs
+System.Web.UI.WebControls/MenuListRenderer.cs
 System.Web.UI.WebControls/MenuRenderingMode.cs
+System.Web.UI.WebControls/NamedCssStyleCollection.cs
 System.Web.UI.WebControls/RouteParameter.cs
+System.Web.UI.WebControls/StyleBlock.cs
 System.Web.UI.WebControls/WizardLayoutContainer.cs
 System.Web.UI.WebControls/WizardLayoutNavigationContainer.cs
 System.Web.Util/RequestValidationSource.cs

Some files were not shown because too many files changed in this diff