3 // Marek Habersack <grendel@twistedcode.net>
5 // (C) 2010 Novell, Inc (http://novell.com)
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System.Collections;
29 using System.Collections.Generic;
31 using System.Globalization;
35 using System.Web.UI.HtmlControls;
37 namespace System.Web.UI.WebControls
39 sealed class MenuListRenderer : BaseMenuRenderer
41 bool haveDynamicPopOut;
43 public override HtmlTextWriterTag Tag {
44 get { return HtmlTextWriterTag.Div; }
48 public MenuListRenderer (Menu owner)
53 public override void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script)
56 script.AppendFormat ("new Sys.WebForms.Menu ({{ element: '{0}', disappearAfter: {1}, orientation: '{2}', tabIndex: {3}, disabled: {4} }});",
58 ClientScriptManager.GetScriptLiteral (owner.DisappearAfter),
59 owner.Orientation.ToString ().ToLowerInvariant (),
60 ClientScriptManager.GetScriptLiteral (owner.TabIndex),
61 (!owner.Enabled).ToString ().ToLowerInvariant ());
63 Type mt = typeof (Menu);
64 if (!csm.IsClientScriptIncludeRegistered (mt, "MenuModern.js")) {
65 string url = csm.GetWebResourceUrl (mt, "MenuModern.js");
66 csm.RegisterClientScriptInclude (mt, "MenuModern.js", url);
69 if (!owner.IncludeStyleBlock)
73 throw new InvalidOperationException ("Using Menu.IncludeStyleBlock requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
75 StyleBlock block = new StyleBlock (owner.ClientID);
76 Style style = owner.ControlStyle;
77 bool horizontal = owner.Orientation == Orientation.Horizontal;
79 block.RegisterStyle (style);
81 // #MenuId img.icon { border-style:none;vertical-align:middle; }
82 block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.icon")
83 .Add (HtmlTextWriterStyle.VerticalAlign, "middle");
85 // #MenuId img.separator { border-style:none;display:block; }
86 block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.separator")
87 .Add (HtmlTextWriterStyle.Display, "block");
89 // #MenuId img.horizontal-separator { border-style:none;vertical-align:middle; }
91 block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.horizontal-separator")
92 .Add (HtmlTextWriterStyle.VerticalAlign, "middle");
94 // #MenuId ul { list-style:none;margin:0;padding:0;width:auto; }
95 block.RegisterStyle (HtmlTextWriterStyle.ListStyleType, "none", "ul")
96 .Add (HtmlTextWriterStyle.Margin, "0")
97 .Add (HtmlTextWriterStyle.Padding, "0")
98 .Add (HtmlTextWriterStyle.Width, "auto");
100 SubMenuStyle sms = owner.StaticMenuStyleInternal;
102 // #MenuId ul.static { ... }
103 block.RegisterStyle (sms, "ul.static");
106 // #MenuId ul.dynamic { ...; z-index:1; ... }
107 NamedCssStyleCollection css = block.RegisterStyle ("ul.dynamic");
108 sms = owner.DynamicMenuStyleInternal;
110 sms.ForeColor = Color.Empty;
114 css.Add (HtmlTextWriterStyle.ZIndex, "1");
115 int num = owner.DynamicHorizontalOffset;
117 css.Add (HtmlTextWriterStyle.MarginLeft, num + "px");
118 num = owner.DynamicVerticalOffset;
120 css.Add (HtmlTextWriterStyle.MarginTop, num + "px");
122 // BUG: rendering of LevelSubMenuStyles throws InvalidCastException on .NET
123 // but I suspect the code it is supposed to generate is as follows:
125 // #MenuId ul.levelX { ... }
127 // so we will just ignore the bug and go with the above code.
128 RenderLevelStyles (block, num, owner.LevelSubMenuStyles, "ul.level");
130 // #MenuId a { text-decoration:none;white-space:nowrap;display:block; }
131 block.RegisterStyle (HtmlTextWriterStyle.TextDecoration, "none", "a")
132 .Add (HtmlTextWriterStyle.WhiteSpace, "nowrap")
133 .Add (HtmlTextWriterStyle.Display, "block");
135 // #MenuId a.static { ... }
136 RenderAnchorStyle (block, owner.StaticMenuItemStyleInternal, "a.static");
138 // #MenuId a.popout { background-image:url("...");background-repeat:no-repeat;background-position:right center;padding-right:14px; }
139 bool needDynamicPopOut = false;
140 string str = owner.StaticPopOutImageUrl;
143 string urlFormat = "url(\"{0}\")";
144 if (String.IsNullOrEmpty (str)) {
145 if (owner.StaticEnableDefaultPopOutImage)
146 css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, GetArrowResourceUrl (owner)), "a.popout");
148 needDynamicPopOut = true;
150 css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout");
151 needDynamicPopOut = true;
155 css.Add ("background-repeat", "no-repeat")
156 .Add ("background-position", "right center")
157 .Add (HtmlTextWriterStyle.PaddingRight, "14px");
159 // #MenuId a.popout-dynamic { background:url("...") no-repeat right center;padding-right:14px; }
160 str = owner.DynamicPopOutImageUrl;
161 bool haveDynamicUrl = !String.IsNullOrEmpty (str);
163 if (needDynamicPopOut || haveDynamicUrl) {
164 urlFormat = "url(\"{0}\") no-repeat right center";
165 if (!haveDynamicUrl) {
166 if (owner.DynamicEnableDefaultPopOutImage)
167 css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, GetArrowResourceUrl (owner)), "a.popout-dynamic");
169 css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout-dynamic");
172 haveDynamicPopOut = true;
173 css.Add (HtmlTextWriterStyle.PaddingRight, "14px");
176 // #MenuId a.dynamic { ... }
177 RenderAnchorStyle (block, owner.DynamicMenuItemStyleInternal, "a.dynamic");
179 num = owner.StaticDisplayLevels;
180 Unit ssmi = owner.StaticSubMenuIndent;
184 if (ssmi == Unit.Empty) {
188 unitName = Unit.GetExtension (ssmi.Type);
192 // #MenuId a.levelX { ... }
193 RenderLevelStyles (block, num, owner.LevelMenuItemStyles, "a.level", unitName, indent);
195 // #MenuId a.selected.levelX { ... }
196 RenderLevelStyles (block, num, owner.LevelSelectedStyles, "a.selected.level");
198 // #MenuId a.static.selected { ...;text-decoration:none; }
199 RenderAnchorStyle (block, owner.StaticSelectedStyleInternal, "a.static.selected");
201 // #MenuId a.dynamic.selected { ...;text-decoration:none;border-style:none; }
202 RenderAnchorStyle (block, owner.DynamicSelectedStyleInternal, "a.dynamic.selected");
204 // #MenuId a.static.highlighted { ... }
205 style = owner.StaticHoverStyleInternal;
207 block.RegisterStyle (style, "a.static.highlighted");
209 // #MenuId a.dynamic.highlighted { ... }
210 style = owner.DynamicHoverStyleInternal;
212 block.RegisterStyle (style, "a.dynamic.highlighted");
214 head.Controls.Add (block);
217 public override void RenderBeginTag (HtmlTextWriter writer, string skipLinkText)
221 // <a href="#ID_SkipLink">
222 writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + owner.ClientID + "_SkipLink");
223 writer.RenderBeginTag (HtmlTextWriterTag.A);
225 // <img alt="" height="0" width="0" src="" style="border-width:0px;"/>
226 writer.AddAttribute (HtmlTextWriterAttribute.Alt, skipLinkText);
227 Page page = owner.Page;
228 ClientScriptManager csm = page != null ? page.ClientScript : new ClientScriptManager (null);
230 writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
231 writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
232 writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
234 writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
235 writer.RenderBeginTag (HtmlTextWriterTag.Img);
236 writer.RenderEndTag ();
238 writer.RenderEndTag (); // </a>
241 public override void RenderEndTag (HtmlTextWriter writer)
245 public override void AddAttributesToRender (HtmlTextWriter writer)
250 public override void RenderContents (HtmlTextWriter writer)
253 MenuItemCollection items = owner.Items;
254 owner.RenderMenu (writer, items, owner.Orientation == Orientation.Vertical, false, 0, items.Count > 1);
257 public override void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
259 if (dynamic || menuLevel == 0) {
260 var style = new SubMenuStyle ();
261 AddCssClass (style, "level" + (menuLevel + 1));
262 FillMenuStyle (null, dynamic, menuLevel, style);
263 style.AddAttributesToRender (writer);
264 writer.RenderBeginTag (HtmlTextWriterTag.Ul);
268 public override void RenderMenuEndTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
270 if (dynamic || menuLevel == 0)
271 base.RenderMenuEndTag (writer, dynamic, menuLevel);
274 public override void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast)
277 int count = items.Count;
278 var oc = new OwnerContext (this);
280 for (int n = 0; n < count; n++) {
281 MenuItem item = items [n];
282 Adapters.MenuAdapter adapter = owner.Adapter as Adapters.MenuAdapter;
284 adapter.RenderItem (writer, item, n);
286 RenderMenuItem (writer, item, vertical, (n + 1) == count ? notLast : true, n == 0, oc);
290 protected override void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc)
293 bool displayChildren = owner.DisplayChildren (item);
294 bool isDynamicItem = IsDynamicItem (owner, item);
295 int itemLevel = item.Depth + 1;
298 writer.RenderBeginTag (HtmlTextWriterTag.Li);
301 RenderSeparatorImage (owner, writer, oc.DynamicTopSeparatorImageUrl, true);
303 RenderSeparatorImage (owner, writer, oc.StaticTopSeparatorImageUrl, true);
305 var linkStyle = new Style ();
306 if (displayChildren && (isDynamicItem || itemLevel >= oc.StaticDisplayLevels))
307 AddCssClass (linkStyle, isDynamicItem && haveDynamicPopOut ? "popout-dynamic" : "popout");
308 AddCssClass (linkStyle, "level" + itemLevel);
310 MenuItemStyleCollection levelStyles = oc.LevelMenuItemStyles;
311 if (levelStyles != null && levelStyles.Count >= itemLevel) {
312 MenuItemStyle style = levelStyles [itemLevel - 1];
313 string cssClass = style.CssClass;
314 if (!String.IsNullOrEmpty (cssClass))
315 AddCssClass (linkStyle, cssClass);
318 if (owner.SelectedItem == item)
319 AddCssClass (linkStyle, "selected");
322 if (!String.IsNullOrEmpty (str))
323 writer.AddAttribute ("title", str);
324 linkStyle.AddAttributesToRender (writer);
325 RenderItemHref (owner, writer, item);
326 writer.RenderBeginTag (HtmlTextWriterTag.A);
327 owner.RenderItemContent (writer, item, isDynamicItem);
328 writer.RenderEndTag ();
330 str = item.SeparatorImageUrl;
331 if (String.IsNullOrEmpty (str)) {
333 str = oc.DynamicBottomSeparatorImageUrl;
335 str = oc.StaticBottomSeparatorImageUrl;
337 RenderSeparatorImage (owner, writer, str, true);
339 // if (itemLevel == 1)
340 // writer.RenderEndTag (); // </li>
343 owner.RenderMenu (writer, item.ChildItems, vertical, isDynamicItem, itemLevel, notLast);
346 writer.RenderEndTag (); // </li>
349 public override bool IsDynamicItem (Menu owner, MenuItem item)
352 throw new ArgumentNullException ("owner");
355 throw new ArgumentNullException ("item");
357 return item.Depth + 1 >= Owner.StaticDisplayLevels;
360 NamedCssStyleCollection RenderAnchorStyle (StyleBlock block, Style style, string styleName)
362 if (style == null || block == null)
365 style.AlwaysRenderTextDecoration = true;
366 NamedCssStyleCollection css = block.RegisterStyle (style, styleName);
367 if (style.BorderStyle == BorderStyle.NotSet)
368 css.Add (HtmlTextWriterStyle.BorderStyle, "none");
373 void RenderLevelStyles (StyleBlock block, int num, IList levelStyles, string name, string unitName = null, double indent = 0)
375 int stylesCount = levelStyles != null ? levelStyles.Count : 0;
376 bool haveStyles = stylesCount > 0;
377 if (!haveStyles || block == null)
380 NamedCssStyleCollection css;
382 bool haveIndent = !String.IsNullOrEmpty (unitName) && indent != 0;
383 for (int i = 0; i < stylesCount; i++) {
384 if ((i == 0 && !haveStyles))
387 css = block.RegisterStyle (name + (i + 1));
388 if (haveStyles && stylesCount > i) {
389 style = levelStyles [i] as Style;
391 style.AlwaysRenderTextDecoration = true;
392 css.CopyFrom (style.GetStyleAttributes (null));
396 if (haveIndent && i > 0 && i < num) {
397 css.Add (HtmlTextWriterStyle.PaddingLeft, indent.ToString (CultureInfo.InvariantCulture) + unitName);