Merge branch 'cecil-light'
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / MenuListRenderer.cs
1 //
2 // Authors:
3 //      Marek Habersack <grendel@twistedcode.net>
4 //
5 // (C) 2010 Novell, Inc (http://novell.com)
6 //
7 //
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:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
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.
26 //
27 using System;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.Drawing;
31 using System.Globalization;
32 using System.Text;
33 using System.Web;
34 using System.Web.UI;
35 using System.Web.UI.HtmlControls;
36
37 namespace System.Web.UI.WebControls
38 {
39         sealed class MenuListRenderer : BaseMenuRenderer
40         {
41                 bool haveDynamicPopOut;
42                 
43                 public override HtmlTextWriterTag Tag {
44                         get { return HtmlTextWriterTag.Div; }
45                         
46                 }
47                 
48                 public MenuListRenderer (Menu owner)
49                         : base (owner)
50                 {
51                 }
52
53                 public override void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script)
54                 {
55                         Menu owner = Owner;
56                         script.AppendFormat ("new Sys.WebForms.Menu ({{ element: '{0}', disappearAfter: {1}, orientation: '{2}', tabIndex: {3}, disabled: {4} }});",
57                                              owner.ClientID,
58                                              ClientScriptManager.GetScriptLiteral (owner.DisappearAfter),
59                                              owner.Orientation.ToString ().ToLowerInvariant (),
60                                              ClientScriptManager.GetScriptLiteral (owner.TabIndex),
61                                              (!owner.Enabled).ToString ().ToLowerInvariant ());
62
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);
67                         }
68                         
69                         if (!owner.IncludeStyleBlock)
70                                 return;
71                         
72                         if (head == null)
73                                 throw new InvalidOperationException ("Using Menu.IncludeStyleBlock requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
74
75                         StyleBlock block = new StyleBlock (owner.ClientID);
76                         Style style = owner.ControlStyle;
77                         bool horizontal = owner.Orientation == Orientation.Horizontal;
78                         if (style != null)
79                                 block.RegisterStyle (style);
80                         
81                         // #MenuId img.icon { border-style:none;vertical-align:middle; }
82                         block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.icon")
83                                 .Add (HtmlTextWriterStyle.VerticalAlign, "middle");
84
85                         // #MenuId img.separator { border-style:none;display:block; }
86                         block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.separator")
87                                 .Add (HtmlTextWriterStyle.Display, "block");
88
89                         // #MenuId img.horizontal-separator { border-style:none;vertical-align:middle; }
90                         if (horizontal)
91                                 block.RegisterStyle (HtmlTextWriterStyle.BorderStyle, "none", "img.horizontal-separator")
92                                         .Add (HtmlTextWriterStyle.VerticalAlign, "middle");
93                         
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");
99
100                         SubMenuStyle sms = owner.StaticMenuStyleInternal;
101                         if (sms != null) {
102                                 // #MenuId ul.static { ... }
103                                 block.RegisterStyle (sms, "ul.static");
104                         }
105
106                         // #MenuId ul.dynamic { ...; z-index:1; ... }
107                         NamedCssStyleCollection css = block.RegisterStyle ("ul.dynamic");
108                         sms = owner.DynamicMenuStyleInternal;
109                         if (sms != null) {
110                                 sms.ForeColor = Color.Empty;
111                                 css.Add (sms);
112                         }
113                         
114                         css.Add (HtmlTextWriterStyle.ZIndex, "1");
115                         int num = owner.DynamicHorizontalOffset;
116                         if (num != 0)
117                                 css.Add (HtmlTextWriterStyle.MarginLeft, num + "px");
118                         num = owner.DynamicVerticalOffset;
119                         if (num != 0)
120                                 css.Add (HtmlTextWriterStyle.MarginTop, num + "px");
121
122                         // BUG: rendering of LevelSubMenuStyles throws InvalidCastException on .NET
123                         // but I suspect the code it is supposed to generate is as follows:
124                         //
125                         // #MenuId ul.levelX { ... }
126                         //
127                         // so we will just ignore the bug and go with the above code.
128                         RenderLevelStyles (block, num, owner.LevelSubMenuStyles, "ul.level");
129                         
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");
134
135                         // #MenuId a.static { ... }
136                         RenderAnchorStyle (block, owner.StaticMenuItemStyleInternal, "a.static");
137                         
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;
141
142                         css = null;
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");
147                                 else
148                                         needDynamicPopOut = true;
149                         } else {
150                                 css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout");
151                                 needDynamicPopOut = true;
152                         }
153                         
154                         if (css != null)
155                                 css.Add ("background-repeat", "no-repeat")
156                                         .Add ("background-position", "right center")
157                                         .Add (HtmlTextWriterStyle.PaddingRight, "14px");
158
159                         // #MenuId a.popout-dynamic { background:url("...") no-repeat right center;padding-right:14px; }
160                         str = owner.DynamicPopOutImageUrl;
161                         bool haveDynamicUrl = !String.IsNullOrEmpty (str);
162                         css = null;
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");
168                                 } else
169                                         css = block.RegisterStyle (HtmlTextWriterStyle.BackgroundImage, String.Format (urlFormat, str), "a.popout-dynamic");
170                         }
171                         if (css != null) {
172                                 haveDynamicPopOut = true;
173                                 css.Add (HtmlTextWriterStyle.PaddingRight, "14px");
174                         }
175
176                         // #MenuId a.dynamic { ... }
177                         RenderAnchorStyle (block, owner.DynamicMenuItemStyleInternal, "a.dynamic");
178                         
179                         num = owner.StaticDisplayLevels;
180                         Unit ssmi = owner.StaticSubMenuIndent;
181                         string unitName;
182                         double indent;
183                                 
184                         if (ssmi == Unit.Empty) {
185                                 unitName = "em";
186                                 indent = 1;
187                         } else {
188                                 unitName = Unit.GetExtension (ssmi.Type);
189                                 indent = ssmi.Value;
190                         }
191
192                         // #MenuId a.levelX { ... }
193                         RenderLevelStyles (block, num, owner.LevelMenuItemStyles, "a.level", unitName, indent);
194
195                         // #MenuId a.selected.levelX { ... }
196                         RenderLevelStyles (block, num, owner.LevelSelectedStyles, "a.selected.level");
197
198                         // #MenuId a.static.selected { ...;text-decoration:none; }
199                         RenderAnchorStyle (block, owner.StaticSelectedStyleInternal, "a.static.selected");
200                         
201                         // #MenuId a.dynamic.selected { ...;text-decoration:none;border-style:none; }
202                         RenderAnchorStyle (block, owner.DynamicSelectedStyleInternal, "a.dynamic.selected");
203
204                         // #MenuId a.static.highlighted { ... }
205                         style = owner.StaticHoverStyleInternal;
206                         if (style != null)
207                                 block.RegisterStyle (style, "a.static.highlighted");
208                         
209                         // #MenuId a.dynamic.highlighted { ... }
210                         style = owner.DynamicHoverStyleInternal;
211                         if (style != null)
212                                 block.RegisterStyle (style, "a.dynamic.highlighted");
213                         
214                         head.Controls.Add (block);
215                 }
216                 
217                 public override void RenderBeginTag (HtmlTextWriter writer, string skipLinkText)
218                 {
219                         Menu owner = Owner;
220                         
221                         // <a href="#ID_SkipLink">
222                         writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + owner.ClientID + "_SkipLink");
223                         writer.RenderBeginTag (HtmlTextWriterTag.A);
224                                 
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);
229                                 
230                         writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
231                         writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
232                         writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
233                         
234                         writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
235                         writer.RenderBeginTag (HtmlTextWriterTag.Img);
236                         writer.RenderEndTag ();
237                                 
238                         writer.RenderEndTag (); // </a>
239                 }
240                 
241                 public override void RenderEndTag (HtmlTextWriter writer)
242                 {
243                 }
244                 
245                 public override void AddAttributesToRender (HtmlTextWriter writer)
246                 {
247                         // do nothing
248                 }
249
250                 public override void RenderContents (HtmlTextWriter writer)
251                 {
252                         Menu owner = Owner;
253                         MenuItemCollection items = owner.Items;
254                         owner.RenderMenu (writer, items, owner.Orientation == Orientation.Vertical, false, 0, items.Count > 1);
255                 }
256                 
257                 public override void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
258                 {
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);
265                         }
266                 }
267
268                 public override void RenderMenuEndTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
269                 {
270                         if (dynamic || menuLevel == 0)
271                                 base.RenderMenuEndTag (writer, dynamic, menuLevel);
272                 }
273                 
274                 public override void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast)
275                 {
276                         Menu owner = Owner;
277                         int count = items.Count;
278                         var oc = new OwnerContext (this);
279                         
280                         for (int n = 0; n < count; n++) {
281                                 MenuItem item = items [n];
282                                 Adapters.MenuAdapter adapter = owner.Adapter as Adapters.MenuAdapter;
283                                 if (adapter != null)
284                                         adapter.RenderItem (writer, item, n);
285                                 else
286                                         RenderMenuItem (writer, item, vertical, (n + 1) == count ? notLast : true, n == 0, oc);
287                         }
288                 }
289
290                 protected override void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc)
291                 {
292                         Menu owner = Owner;
293                         bool displayChildren = owner.DisplayChildren (item);
294                         bool isDynamicItem = IsDynamicItem (owner, item);
295                         int itemLevel = item.Depth + 1;
296                         string str;
297                         
298                         writer.RenderBeginTag (HtmlTextWriterTag.Li);
299
300                         if (isDynamicItem)
301                                 RenderSeparatorImage (owner, writer, oc.DynamicTopSeparatorImageUrl, true);
302                         else
303                                 RenderSeparatorImage (owner, writer, oc.StaticTopSeparatorImageUrl, true);
304                         
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);
309
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);
316                         }
317
318                         if (owner.SelectedItem == item)
319                                 AddCssClass (linkStyle, "selected");
320                         
321                         str = item.ToolTip;
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 ();
329
330                         str = item.SeparatorImageUrl;
331                         if (String.IsNullOrEmpty (str)) {
332                                 if (isDynamicItem)
333                                         str = oc.DynamicBottomSeparatorImageUrl;
334                                 else
335                                         str = oc.StaticBottomSeparatorImageUrl;
336                         }
337                         RenderSeparatorImage (owner, writer, str, true);
338
339                         // if (itemLevel == 1)
340                         //      writer.RenderEndTag (); // </li>
341                         
342                         if (displayChildren)
343                                 owner.RenderMenu (writer, item.ChildItems, vertical, isDynamicItem, itemLevel, notLast);
344
345                         if (itemLevel > 1)
346                                 writer.RenderEndTag (); // </li>
347                 }
348
349                 public override bool IsDynamicItem (Menu owner, MenuItem item)
350                 {
351                         if (owner == null)
352                                 throw new ArgumentNullException ("owner");
353
354                         if (item == null)
355                                 throw new ArgumentNullException ("item");
356                         
357                         return item.Depth + 1 >= Owner.StaticDisplayLevels;
358                 }
359                 
360                 NamedCssStyleCollection RenderAnchorStyle (StyleBlock block, Style style, string styleName)
361                 {
362                         if (style == null || block == null)
363                                 return null;
364
365                         style.AlwaysRenderTextDecoration = true;
366                         NamedCssStyleCollection css = block.RegisterStyle (style, styleName);
367                         if (style.BorderStyle == BorderStyle.NotSet)
368                                 css.Add (HtmlTextWriterStyle.BorderStyle, "none");
369
370                         return css;
371                 }
372
373                 void RenderLevelStyles (StyleBlock block, int num, IList levelStyles, string name, string unitName = null, double indent = 0)
374                 {
375                         int stylesCount = levelStyles != null ? levelStyles.Count : 0;
376                         bool haveStyles = stylesCount > 0;
377                         if (!haveStyles || block == null)
378                                 return;
379
380                         NamedCssStyleCollection css;
381                         Style style;
382                         bool haveIndent = !String.IsNullOrEmpty (unitName) && indent != 0;
383                         for (int i = 0; i < stylesCount; i++) {
384                                 if ((i == 0 && !haveStyles))
385                                         continue;
386                                 
387                                 css = block.RegisterStyle (name + (i + 1));
388                                 if (haveStyles && stylesCount > i) {
389                                         style = levelStyles [i] as Style;
390                                         if (style != null) {
391                                                 style.AlwaysRenderTextDecoration = true;
392                                                 css.CopyFrom (style.GetStyleAttributes (null));
393                                         }
394                                 }
395                                 
396                                 if (haveIndent && i > 0 && i < num) {
397                                         css.Add (HtmlTextWriterStyle.PaddingLeft, indent.ToString (CultureInfo.InvariantCulture) + unitName);
398                                         indent += indent;
399                                 }
400                         }
401                 }
402         }
403 }