Merge pull request #1500 from criteo-forks/criteo
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / MenuTableRenderer.cs
1 //
2 // Authors:
3 //      Lluis Sanchez Gual (lluis@novell.com)
4 //      Igor Zelmanovich (igorz@mainsoft.com)
5 //      Marek Habersack <grendel@twistedcode.net>
6 //
7 // (C) 2010 Novell, Inc (http://novell.com)
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Collections.Generic;
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 MenuTableRenderer : BaseMenuRenderer
40         {
41                 const string onPreRenderScript = "var {0} = new Object ();\n{0}.webForm = {1};\n{0}.disappearAfter = {2};\n{0}.vertical = {3};";
42
43                 public override HtmlTextWriterTag Tag {
44                         get { return HtmlTextWriterTag.Table; }
45                         
46                 }
47                 
48                 public MenuTableRenderer (Menu owner)
49                         : base (owner)
50                 {
51                 }
52                 
53                 public override void AddAttributesToRender (HtmlTextWriter writer)
54                 {
55                         writer.AddAttribute ("cellpadding", "0", false);
56                         writer.AddAttribute ("cellspacing", "0", false);
57                         writer.AddAttribute ("border", "0", false);
58
59                         base.AddAttributesToRender (writer);
60                 }
61                 
62                 public override void PreRender (Page page, HtmlHead head, ClientScriptManager csm, string cmenu, StringBuilder script)
63                 {
64                         Menu owner = Owner;
65                         MenuItemStyle staticMenuItemStyle = owner.StaticMenuItemStyleInternal;
66                         SubMenuStyle staticMenuStyle = owner.StaticMenuStyleInternal;
67                         MenuItemStyle dynamicMenuItemStyle = owner.DynamicMenuItemStyleInternal;
68                         SubMenuStyle dynamicMenuStyle = owner.DynamicMenuStyleInternal;
69                         MenuItemStyleCollection levelMenuItemStyles = owner.LevelMenuItemStyles;
70                         List<Style> levelMenuItemLinkStyles = owner.LevelMenuItemLinkStyles;
71                         SubMenuStyleCollection levelSubMenuStyles = owner.LevelSubMenuStylesInternal;
72                         MenuItemStyle staticSelectedStyle = owner.StaticSelectedStyleInternal;
73                         MenuItemStyle dynamicSelectedStyle = owner.DynamicSelectedStyleInternal;
74                         MenuItemStyleCollection levelSelectedStyles = owner.LevelSelectedStylesInternal;
75                         List<Style> levelSelectedLinkStyles = owner.LevelSelectedLinkStyles;
76                         Style staticHoverStyle = owner.StaticHoverStyleInternal;
77                         Style dynamicHoverStyle = owner.DynamicHoverStyleInternal;
78                         
79                         if (!csm.IsClientScriptIncludeRegistered (typeof (Menu), "Menu.js")) {
80                                 string url = csm.GetWebResourceUrl (typeof (Menu), "Menu.js");
81                                 csm.RegisterClientScriptInclude (typeof (Menu), "Menu.js", url);
82                         }
83                         
84                         script.AppendFormat (onPreRenderScript,
85                                              cmenu,
86                                              page.IsMultiForm ? page.theForm : "window",
87                                              ClientScriptManager.GetScriptLiteral (owner.DisappearAfter),
88                                              ClientScriptManager.GetScriptLiteral (owner.Orientation == Orientation.Vertical));
89
90                         if (owner.DynamicHorizontalOffset != 0)
91                                 script.Append (String.Concat (cmenu, ".dho = ", ClientScriptManager.GetScriptLiteral (owner.DynamicHorizontalOffset), ";\n"));
92                         if (owner.DynamicVerticalOffset != 0)
93                                 script.Append (String.Concat (cmenu, ".dvo = ", ClientScriptManager.GetScriptLiteral (owner.DynamicVerticalOffset), ";\n"));
94
95                         // The order in which styles are defined matters when more than one class
96                         // is assigned to an element
97                         RegisterStyle (owner.PopOutBoxStyle, head);
98                         RegisterStyle (owner.ControlStyle, owner.ControlLinkStyle, head);
99                         
100                         if (staticMenuItemStyle != null)
101                                 RegisterStyle (owner.StaticMenuItemStyle, owner.StaticMenuItemLinkStyle, head);
102
103                         if (staticMenuStyle != null)
104                                 RegisterStyle (owner.StaticMenuStyle, head);
105                         
106                         if (dynamicMenuItemStyle != null)
107                                 RegisterStyle (owner.DynamicMenuItemStyle, owner.DynamicMenuItemLinkStyle, head);
108
109                         if (dynamicMenuStyle != null)
110                                 RegisterStyle (owner.DynamicMenuStyle, head);
111
112                         if (levelMenuItemStyles != null && levelMenuItemStyles.Count > 0) {
113                                 levelMenuItemLinkStyles = new List<Style> (levelMenuItemStyles.Count);
114                                 foreach (Style style in levelMenuItemStyles) {
115                                         Style linkStyle = new Style ();
116                                         levelMenuItemLinkStyles.Add (linkStyle);
117                                         RegisterStyle (style, linkStyle, head);
118                                 }
119                         }
120                 
121                         if (levelSubMenuStyles != null)
122                                 foreach (Style style in levelSubMenuStyles)
123                                         RegisterStyle (style, head);
124
125                         if (staticSelectedStyle != null)
126                                 RegisterStyle (staticSelectedStyle, owner.StaticSelectedLinkStyle, head);
127                         
128                         if (dynamicSelectedStyle != null)
129                                 RegisterStyle (dynamicSelectedStyle, owner.DynamicSelectedLinkStyle, head);
130
131                         if (levelSelectedStyles != null && levelSelectedStyles.Count > 0) {
132                                 levelSelectedLinkStyles = new List<Style> (levelSelectedStyles.Count);
133                                 foreach (Style style in levelSelectedStyles) {
134                                         Style linkStyle = new Style ();
135                                         levelSelectedLinkStyles.Add (linkStyle);
136                                         RegisterStyle (style, linkStyle, head);
137                                 }
138                         }
139                         
140                         if (staticHoverStyle != null) {
141                                 if (head == null)
142                                         throw new InvalidOperationException ("Using Menu.StaticHoverStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
143                                 RegisterStyle (staticHoverStyle, owner.StaticHoverLinkStyle, head);
144                                 script.Append (String.Concat (cmenu, ".staticHover = ", ClientScriptManager.GetScriptLiteral (staticHoverStyle.RegisteredCssClass), ";\n"));
145                                 script.Append (String.Concat (cmenu, ".staticLinkHover = ", ClientScriptManager.GetScriptLiteral (owner.StaticHoverLinkStyle.RegisteredCssClass), ";\n"));
146                         }
147                         
148                         if (dynamicHoverStyle != null) {
149                                 if (head == null)
150                                         throw new InvalidOperationException ("Using Menu.DynamicHoverStyle requires Page.Header to be non-null (e.g. <head runat=\"server\" />).");
151                                 RegisterStyle (dynamicHoverStyle, owner.DynamicHoverLinkStyle, head);
152                                 script.Append (String.Concat (cmenu, ".dynamicHover = ", ClientScriptManager.GetScriptLiteral (dynamicHoverStyle.RegisteredCssClass), ";\n"));
153                                 script.Append (String.Concat (cmenu, ".dynamicLinkHover = ", ClientScriptManager.GetScriptLiteral (owner.DynamicHoverLinkStyle.RegisteredCssClass), ";\n"));
154                         }
155                 }
156
157                 public override void RenderBeginTag (HtmlTextWriter writer, string skipLinkText)
158                 {
159                         Menu owner = Owner;
160                         
161                         // <a href="#ID_SkipLink">
162                         writer.AddAttribute (HtmlTextWriterAttribute.Href, "#" + owner.ClientID + "_SkipLink");
163                         writer.RenderBeginTag (HtmlTextWriterTag.A);
164                                 
165                         // <img alt="" height="0" width="0" src="" style="border-width:0px;"/>
166                         writer.AddAttribute (HtmlTextWriterAttribute.Alt, skipLinkText);
167                         writer.AddAttribute (HtmlTextWriterAttribute.Height, "0");
168                         writer.AddAttribute (HtmlTextWriterAttribute.Width, "0");
169                                 
170                         Page page = owner.Page;
171                         ClientScriptManager csm = page != null ? page.ClientScript : new ClientScriptManager (null);
172                                 
173                         writer.AddAttribute (HtmlTextWriterAttribute.Src, csm.GetWebResourceUrl (typeof (SiteMapPath), "transparent.gif"));
174                         writer.AddStyleAttribute (HtmlTextWriterStyle.BorderWidth, "0px");
175                         writer.RenderBeginTag (HtmlTextWriterTag.Img);
176                         writer.RenderEndTag ();
177                                 
178                         writer.RenderEndTag (); // </a>
179                 }
180                 
181                 public override void RenderEndTag (HtmlTextWriter writer)
182                 {
183                         Menu owner = Owner;
184                         if (owner.StaticDisplayLevels == 1 && owner.MaximumDynamicDisplayLevels > 0)
185                                 owner.RenderDynamicMenu (writer, owner.Items);
186                 }
187
188                 public override void RenderContents (HtmlTextWriter writer)
189                 {
190                         Menu owner = Owner;
191                         RenderMenuBody (writer, owner.Items, owner.Orientation == Orientation.Vertical, false, false);
192                 }
193                 
194                 void RenderMenuBeginTagAttributes (HtmlTextWriter writer, bool dynamic, int menuLevel)
195                 {
196                         writer.AddAttribute ("cellpadding", "0", false);
197                         writer.AddAttribute ("cellspacing", "0", false);
198                         writer.AddAttribute ("border", "0", false);
199
200                         if (!dynamic) {
201                                 SubMenuStyle style = new SubMenuStyle ();
202                                 FillMenuStyle (null, dynamic, menuLevel, style);
203                                 style.AddAttributesToRender (writer);
204                         }
205                 }
206                 
207                 public override void RenderMenuBeginTag (HtmlTextWriter writer, bool dynamic, int menuLevel)
208                 {
209                         RenderMenuBeginTagAttributes (writer, dynamic, menuLevel);
210                         writer.RenderBeginTag (HtmlTextWriterTag.Table);
211                 }
212
213                 void RenderMenuItemSpacing (HtmlTextWriter writer, Unit itemSpacing, bool vertical)
214                 {
215                         if (vertical) {
216                                 writer.AddStyleAttribute ("height", itemSpacing.ToString ());
217                                 writer.RenderBeginTag (HtmlTextWriterTag.Tr);
218                                 writer.RenderBeginTag (HtmlTextWriterTag.Td);
219                                 writer.RenderEndTag ();
220                                 writer.RenderEndTag ();
221                         } else {
222                                 writer.AddStyleAttribute ("width", itemSpacing.ToString ());
223                                 writer.RenderBeginTag (HtmlTextWriterTag.Td);
224                                 writer.RenderEndTag ();
225                         }
226                 }
227                 
228                 public override void RenderMenuBody (HtmlTextWriter writer, MenuItemCollection items, bool vertical, bool dynamic, bool notLast)
229                 {
230                         Menu owner = Owner;
231                         if (!vertical)
232                                 writer.RenderBeginTag (HtmlTextWriterTag.Tr);
233
234                         int count = items.Count;
235                         var oc = new OwnerContext (this);
236                         
237                         for (int n = 0; n < count; n++) {
238                                 MenuItem item = items [n];
239                                 Adapters.MenuAdapter adapter = owner.Adapter as Adapters.MenuAdapter;
240                                 if (adapter != null)
241                                         adapter.RenderItem (writer, item, n);
242                                 else
243                                         RenderMenuItem (writer, item, vertical, (n + 1) == count ? notLast : true, n == 0, oc);
244                         }
245
246                         if (!vertical)
247                                 writer.RenderEndTag (); // TR
248                 }
249
250                 protected override void RenderMenuItem (HtmlTextWriter writer, MenuItem item, bool vertical, bool notLast, bool isFirst, OwnerContext oc)
251                 {
252                         Menu owner = Owner;
253                         string clientID = oc.ClientID;
254                         bool displayChildren = owner.DisplayChildren (item);
255                         bool dynamicChildren = displayChildren && (item.Depth + 1 >= oc.StaticDisplayLevels);
256                         bool isDynamicItem = IsDynamicItem (owner, item);
257                         bool isVertical = oc.IsVertical || isDynamicItem;
258                         Unit itemSpacing = owner.GetItemSpacing (item, isDynamicItem);
259
260                         if (itemSpacing != Unit.Empty && (item.Depth > 0 || !isFirst))
261                                 RenderMenuItemSpacing (writer, itemSpacing, isVertical);
262
263                         if(!String.IsNullOrEmpty(item.ToolTip))
264                                 writer.AddAttribute (HtmlTextWriterAttribute.Title, item.ToolTip);
265                         if (isVertical)
266                                 writer.RenderBeginTag (HtmlTextWriterTag.Tr);
267
268                         string parentId = isDynamicItem ? "'" + item.Parent.Path + "'" : "null";
269                         if (dynamicChildren) {
270                                 writer.AddAttribute ("onmouseover",
271                                                      "javascript:Menu_OverItem ('" + clientID + "','" + item.Path + "'," + parentId + ")");
272                                 writer.AddAttribute ("onmouseout",
273                                                      "javascript:Menu_OutItem ('" + clientID + "','" + item.Path + "')");
274                         } else if (isDynamicItem) {
275                                 writer.AddAttribute ("onmouseover",
276                                                      "javascript:Menu_OverDynamicLeafItem ('" + clientID + "','" + item.Path + "'," + parentId + ")");
277                                 writer.AddAttribute ("onmouseout",
278                                                      "javascript:Menu_OutItem ('" + clientID + "','" + item.Path + "'," + parentId + ")");
279                         } else {
280                                 writer.AddAttribute ("onmouseover",
281                                                      "javascript:Menu_OverStaticLeafItem ('" + clientID + "','" + item.Path + "')");
282                                 writer.AddAttribute ("onmouseout",
283                                                      "javascript:Menu_OutItem ('" + clientID + "','" + item.Path + "')");
284                         }
285
286                         writer.RenderBeginTag (HtmlTextWriterTag.Td);
287
288                         // Top separator image
289
290                         if (isDynamicItem)
291                                 RenderSeparatorImage (owner, writer, oc.DynamicTopSeparatorImageUrl, false);
292                         else
293                                 RenderSeparatorImage (owner, writer, oc.StaticTopSeparatorImageUrl, false);
294
295                         // Menu item box
296                         
297                         MenuItemStyle style = new MenuItemStyle ();
298                                 
299                         if (oc.Header != null) {
300                                 // styles are registered
301                                 if (!isDynamicItem && oc.StaticMenuItemStyle != null) {
302                                         AddCssClass (style, oc.StaticMenuItemStyle.CssClass);
303                                         AddCssClass (style, oc.StaticMenuItemStyle.RegisteredCssClass);
304                                 }
305                                 if (isDynamicItem && oc.DynamicMenuItemStyle != null) {
306                                         AddCssClass (style, oc.DynamicMenuItemStyle.CssClass);
307                                         AddCssClass (style, oc.DynamicMenuItemStyle.RegisteredCssClass);
308                                 }
309                                 if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth) {
310                                         AddCssClass (style, oc.LevelMenuItemStyles [item.Depth].CssClass);
311                                         AddCssClass (style, oc.LevelMenuItemStyles [item.Depth].RegisteredCssClass);
312                                 }
313                                 if (item == oc.SelectedItem) {
314                                         if (!isDynamicItem && oc.StaticSelectedStyle != null) {
315                                                 AddCssClass (style, oc.StaticSelectedStyle.CssClass);
316                                                 AddCssClass (style, oc.StaticSelectedStyle.RegisteredCssClass);
317                                         }
318                                         if (isDynamicItem && oc.DynamicSelectedStyle != null) {
319                                                 AddCssClass (style, oc.DynamicSelectedStyle.CssClass);
320                                                 AddCssClass (style, oc.DynamicSelectedStyle.RegisteredCssClass);
321                                         }
322                                         if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth) {
323                                                 AddCssClass (style, oc.LevelSelectedStyles [item.Depth].CssClass);
324                                                 AddCssClass (style, oc.LevelSelectedStyles [item.Depth].RegisteredCssClass);
325                                         }
326                                 }
327                         } else {
328                                 // styles are not registered
329                                 if (!isDynamicItem && oc.StaticMenuItemStyle != null)
330                                         style.CopyFrom (oc.StaticMenuItemStyle);
331                                 if (isDynamicItem && oc.DynamicMenuItemStyle != null)
332                                         style.CopyFrom (oc.DynamicMenuItemStyle);
333                                 if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth)
334                                         style.CopyFrom (oc.LevelMenuItemStyles [item.Depth]);
335                                 if (item == oc.SelectedItem) {
336                                         if (!isDynamicItem && oc.StaticSelectedStyle != null)
337                                                 style.CopyFrom (oc.StaticSelectedStyle);
338                                         if (isDynamicItem && oc.DynamicSelectedStyle != null)
339                                                 style.CopyFrom (oc.DynamicSelectedStyle);
340                                         if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth)
341                                                 style.CopyFrom (oc.LevelSelectedStyles [item.Depth]);
342                                 }
343                         }
344                         style.AddAttributesToRender (writer);
345
346                         writer.AddAttribute ("id", GetItemClientId (clientID, item, "i"));
347                         writer.AddAttribute ("cellpadding", "0", false);
348                         writer.AddAttribute ("cellspacing", "0", false);
349                         writer.AddAttribute ("border", "0", false);
350                         writer.AddAttribute ("width", "100%", false);
351                         writer.RenderBeginTag (HtmlTextWriterTag.Table);
352                         writer.RenderBeginTag (HtmlTextWriterTag.Tr);
353
354                         // Menu item text
355
356                         if (isVertical)
357                                 writer.AddStyleAttribute (HtmlTextWriterStyle.Width, "100%");
358                         if (!owner.ItemWrap)
359                                 writer.AddStyleAttribute ("white-space", "nowrap");
360                         writer.RenderBeginTag (HtmlTextWriterTag.Td);
361
362                         RenderItemHref (owner, writer, item);
363                         
364                         Style linkStyle = new Style ();
365                         if (oc.Header != null) {
366                                 // styles are registered
367                                 AddCssClass (linkStyle, oc.ControlLinkStyle.RegisteredCssClass);
368
369                                 if (!isDynamicItem && oc.StaticMenuItemStyle != null) {
370                                         AddCssClass (linkStyle, oc.StaticMenuItemStyle.CssClass);
371                                         AddCssClass (linkStyle, oc.StaticMenuItemLinkStyle.RegisteredCssClass);
372                                 }
373                                 if (isDynamicItem && oc.DynamicMenuItemStyle != null) {
374                                         AddCssClass (linkStyle, oc.DynamicMenuItemStyle.CssClass);
375                                         AddCssClass (linkStyle, oc.DynamicMenuItemLinkStyle.RegisteredCssClass);
376                                 }
377                                 if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth) {
378                                         AddCssClass (linkStyle, oc.LevelMenuItemStyles [item.Depth].CssClass);
379                                         AddCssClass (linkStyle, oc.LevelMenuItemLinkStyles [item.Depth].RegisteredCssClass);
380                                 }
381                                 if (item == oc.SelectedItem) {
382                                         if (!isDynamicItem && oc.StaticSelectedStyle != null) {
383                                                 AddCssClass (linkStyle, oc.StaticSelectedStyle.CssClass);
384                                                 AddCssClass (linkStyle, oc.StaticSelectedLinkStyle.RegisteredCssClass);
385                                         }
386                                         if (isDynamicItem && oc.DynamicSelectedStyle != null) {
387                                                 AddCssClass (linkStyle, oc.DynamicSelectedStyle.CssClass);
388                                                 AddCssClass (linkStyle, oc.DynamicSelectedLinkStyle.RegisteredCssClass);
389                                         }
390                                         if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth) {
391                                                 AddCssClass (linkStyle, oc.LevelSelectedStyles [item.Depth].CssClass);
392                                                 AddCssClass (linkStyle, oc.LevelSelectedLinkStyles [item.Depth].RegisteredCssClass);
393                                         }
394                                 }
395                         } else {
396                                 // styles are not registered
397                                 linkStyle.CopyFrom (oc.ControlLinkStyle);
398
399                                 if (!isDynamicItem && oc.StaticMenuItemStyle != null)
400                                         linkStyle.CopyFrom (oc.StaticMenuItemLinkStyle);
401                                 if (isDynamicItem && oc.DynamicMenuItemStyle != null)
402                                         linkStyle.CopyFrom (oc.DynamicMenuItemLinkStyle);
403                                 if (oc.LevelMenuItemStyles != null && oc.LevelMenuItemStyles.Count > item.Depth)
404                                         linkStyle.CopyFrom (oc.LevelMenuItemLinkStyles [item.Depth]);
405                                 if (item == oc.SelectedItem) {
406                                         if (!isDynamicItem && oc.StaticSelectedStyle != null)
407                                                 linkStyle.CopyFrom (oc.StaticSelectedLinkStyle);
408                                         if (isDynamicItem && oc.DynamicSelectedStyle != null)
409                                                 linkStyle.CopyFrom (oc.DynamicSelectedLinkStyle);
410                                         if (oc.LevelSelectedStyles != null && oc.LevelSelectedStyles.Count > item.Depth)
411                                                 linkStyle.CopyFrom (oc.LevelSelectedLinkStyles [item.Depth]);
412                                 }
413
414                                 linkStyle.AlwaysRenderTextDecoration = true;
415                         }
416                         linkStyle.AddAttributesToRender (writer);
417
418                         writer.AddAttribute ("id", GetItemClientId (clientID, item, "l"));
419                         
420                         if (item.Depth > 0 && !isDynamicItem) {
421                                 double value;
422                                 Unit unit = oc.StaticSubMenuIndent;
423                                 if (unit == Unit.Empty)
424                                         value = 16;
425                                 else
426                                         value = unit.Value;
427                                 Unit indent = new Unit (value * item.Depth, oc.StaticSubMenuIndent.Type);
428                                 writer.AddStyleAttribute (HtmlTextWriterStyle.MarginLeft, indent.ToString ());
429                         }
430                         writer.RenderBeginTag (HtmlTextWriterTag.A);
431                         owner.RenderItemContent (writer, item, isDynamicItem);
432                         writer.RenderEndTag (); // A
433
434                         writer.RenderEndTag (); // TD
435
436                         // Popup image
437
438                         if (dynamicChildren) {
439                                 string popOutImage = GetPopOutImage (owner, item, isDynamicItem);
440                                 if (popOutImage != null) {
441                                         writer.RenderBeginTag (HtmlTextWriterTag.Td);
442                                         writer.AddAttribute ("src", owner.ResolveClientUrl (popOutImage));
443                                         writer.AddAttribute ("border", "0");
444                                         string toolTip = String.Format (isDynamicItem ? oc.DynamicPopOutImageTextFormatString : oc.StaticPopOutImageTextFormatString, item.Text);
445                                         writer.AddAttribute (HtmlTextWriterAttribute.Alt, toolTip);
446                                         writer.RenderBeginTag (HtmlTextWriterTag.Img);
447                                         writer.RenderEndTag (); // IMG
448                                         writer.RenderEndTag (); // TD
449                                 }
450                         }
451
452                         writer.RenderEndTag (); // TR
453                         writer.RenderEndTag (); // TABLE
454                         
455                         writer.RenderEndTag (); // TD
456
457                         if (!isVertical && itemSpacing == Unit.Empty && (notLast || (displayChildren && !dynamicChildren))) {
458                                 writer.AddStyleAttribute ("width", "3px");
459                                 writer.RenderBeginTag (HtmlTextWriterTag.Td);
460                                 writer.RenderEndTag ();
461                         }
462                         
463                         // Bottom separator image
464                         string separatorImg = item.SeparatorImageUrl;
465                         if (separatorImg.Length == 0) {
466                                 if (isDynamicItem)
467                                         separatorImg = oc.DynamicBottomSeparatorImageUrl;
468                                 else
469                                         separatorImg = oc.StaticBottomSeparatorImageUrl;
470                         }
471                         
472                         if (separatorImg.Length > 0) {
473                                 if (!isVertical)
474                                         writer.RenderBeginTag (HtmlTextWriterTag.Td);
475                                 RenderSeparatorImage (owner, writer, separatorImg, false);
476                                 if (!isVertical)
477                                         writer.RenderEndTag (); // TD
478                         }
479
480                         if (isVertical)
481                                 writer.RenderEndTag (); // TR
482
483                         if (itemSpacing != Unit.Empty)
484                                 RenderMenuItemSpacing (writer, itemSpacing, isVertical);
485
486                         // Submenu
487
488                         if (displayChildren && !dynamicChildren) {
489                                 if (isVertical)
490                                         writer.RenderBeginTag (HtmlTextWriterTag.Tr);
491                                 writer.RenderBeginTag (HtmlTextWriterTag.Td);
492                                 writer.AddAttribute ("width", "100%");
493                                 owner.RenderMenu (writer, item.ChildItems, vertical, false, item.Depth + 1, notLast);
494                                 if (item.Depth + 2 == oc.StaticDisplayLevels)
495                                         owner.RenderDynamicMenu (writer, item.ChildItems);
496                                 writer.RenderEndTag (); // TD
497                                 if (isVertical)
498                                         writer.RenderEndTag (); // TR
499                         }
500                 }
501
502                 public override bool IsDynamicItem (Menu owner, MenuItem item)
503                 {
504                         if (owner == null)
505                                 throw new ArgumentNullException ("owner");
506
507                         if (item == null)
508                                 throw new ArgumentNullException ("item");
509                         
510                         return item.Depth + 1 > owner.StaticDisplayLevels;
511                 }
512         }
513 }