click-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sa...@apache.org
Subject svn commit: r928164 - /click/trunk/click/extras/src/org/apache/click/extras/control/Menu.java
Date Sat, 27 Mar 2010 07:30:17 GMT
Author: sabob
Date: Sat Mar 27 07:30:16 2010
New Revision: 928164

URL: http://svn.apache.org/viewvc?rev=928164&view=rev
Log:
added menu i18n support and root menu rendering option

Modified:
    click/trunk/click/extras/src/org/apache/click/extras/control/Menu.java

Modified: click/trunk/click/extras/src/org/apache/click/extras/control/Menu.java
URL: http://svn.apache.org/viewvc/click/trunk/click/extras/src/org/apache/click/extras/control/Menu.java?rev=928164&r1=928163&r2=928164&view=diff
==============================================================================
--- click/trunk/click/extras/src/org/apache/click/extras/control/Menu.java (original)
+++ click/trunk/click/extras/src/org/apache/click/extras/control/Menu.java Sat Mar 27 07:30:16
2010
@@ -20,6 +20,7 @@ package org.apache.click.extras.control;
 
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.StringTokenizer;
 
@@ -565,10 +566,23 @@ public class Menu extends AbstractContro
             return label;
         }
 
-        if (getName() != null) {
-            label = ClickUtils.toLabel(getName());
+        String name = getName();
+
+        if (name != null) {
+            Menu root = findRootMenu();
+
+            // Use root menu messages to lookup the label
+            String i18nLabel = root.getMessage(name + ".label");
+
+            if (i18nLabel == null) {
+                i18nLabel = ClickUtils.toLabel(name);
+            }
+
+            // NOTE: don't cache the i18nLabel, since menus are often cached
+            // statically
+            return i18nLabel;
         }
-        return label;
+        return null;
     }
 
     /**
@@ -797,7 +811,23 @@ public class Menu extends AbstractContro
      * @return the title attribute of the Menu item
      */
     public String getTitle() {
-        return title;
+        // Return cached title if set
+        if (title != null) {
+            return title;
+        }
+
+        String name = getName();
+
+        if (name != null) {
+            // Use root menu messages to lookup the title
+            Menu root = findRootMenu();
+
+            // NOTE: don't cache the i18nTitle, since menus are often cached
+            // statically
+            return root.getMessage(name + ".title");
+        }
+
+        return null;
     }
 
     /**
@@ -834,7 +864,7 @@ public class Menu extends AbstractContro
         } else {
             Context context = getContext();
             if (path == null) {
-                // Guard against rendering "null" in the url
+                // Guard against rendering "null" in the href
                 path = "";
             }
             return context.getResponse().encodeURL(context.getRequest().getContextPath()
+ "/" + path);
@@ -904,6 +934,36 @@ public class Menu extends AbstractContro
     }
 
     /**
+     * Return true if this menu contains the given menu, false otherwise.
+     * <p/>
+     * To test if the given menu is contained, this method will test against
+     * both the menu object reference as well as the menu name.
+     *
+     * @return true if this menu contains the given menu, false otherwise
+     */
+    public boolean contains(Menu menu) {
+        if (hasChildren()) {
+            for (Menu child : getChildren()) {
+
+                // Test against object reference
+                if (child == menu) {
+                    return true;
+                }
+
+                // Test against menu name
+                String childName = child.getName();
+                String menuName = menu.getName();
+                if (childName != null && menuName != null) {
+                    if (childName.equals(menu.getName())) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Find the root menu, or null if no root menu can be found.
      *
      * @return the root menu, or null if no root menu can be found.
@@ -938,9 +998,21 @@ public class Menu extends AbstractContro
     }
 
     /**
-     * Render an anchor tag HTML representation of the Menu. If the menu item
-     * is selected the anchor tag will be rendered with class="selected"
-     * attribute.
+     * Render an HTML representation of the Menu.
+     * <p/>
+     * If this menu is the root menu ({@link #isRoot()} returns true), the menu
+     * and all its submenus (recursively), will be rendered by delegating
+     * rendering to the method
+     * {@link #renderRootMenu(org.apache.click.util.HtmlStringBuffer)}. The menu
+     * structure will be rendered as an HTML List consisting of &lt;ul&gt; and
+     * &lt;li&gt; elements.
+     * <p/>
+     * If this menu is <tt>not</tt> the root menu, this menu will be rendered
+     * on its own by delegating rendering to the method
+     * {@link #renderMenuLink(org.apache.click.util.HtmlStringBuffer, org.apache.click.extras.control.Menu)}.
+     * <p/>
+     * By having two render modes one can render the entire menu
+     * automatically, or render each menu item manually using a Velocity macro.
      *
      * @see #toString()
      *
@@ -948,99 +1020,283 @@ public class Menu extends AbstractContro
      */
     @Override
     public void render(HtmlStringBuffer buffer) {
+        if (isRoot()) {
 
-        if (isSeparator()) {
-            buffer.append("<hr/>");
-
+            renderRootMenu(buffer);
         } else {
-            buffer.elementStart("a");
 
-            String id = getAttribute("id");
-            if (id != null) {
-                buffer.appendAttribute("id", id);
+            if (isSeparator()) {
+                renderSeparator(buffer, this);
+            } else {
+                renderMenuLink(buffer, this);
             }
+        }
+    }
 
-            if (getName() != null) {
-                buffer.appendAttribute("name", getName());
-            }
+    /**
+     * Return an HTML representation of the menu.
+     *
+     * @see {@link #render(org.apache.click.util.HtmlStringBuffer)}
+     *
+     * @return an HTML anchor tag representation of the menu
+     */
+    @Override
+    public String toString() {
+        HtmlStringBuffer buffer = new HtmlStringBuffer();
+        render(buffer);
+        return buffer.toString();
+    }
 
-            String href = getHref();
-            buffer.appendAttribute("href", href);
+    // Protected Methods ------------------------------------------------------
 
-            if ("#".equals(href)) {
-                //If hyperlink does not return false here, clicking on it will scroll
-                //to the top of the page.
-                buffer.appendAttribute("onclick", "return false;");
-            }
+    /**
+     * Render an HTML representation of the root menu.
+     *
+     * @param buffer the buffer to render to
+     */
+    protected void renderRootMenu(HtmlStringBuffer buffer) {
+        buffer.elementStart("div");
+        buffer.appendAttribute("id", getId());
+        buffer.appendAttribute("class", "menustyle");
+        buffer.closeTag();
+        buffer.append("\n");
 
-            if (getTarget() != null && getTarget().length() > 0) {
-                buffer.appendAttribute("target", getTarget());
-            }
+        int depth = 0;
+        renderMenuList(buffer, this, depth);
+        buffer.elementEnd("div");
+    }
 
-            if (getTitle() != null && getTitle().length() > 0) {
-                buffer.appendAttributeEscaped("title", getTitle());
-            }
+    /**
+     * Render an html represenatation of the menu list (&lt;ul&gt;) structure.
+     *
+     * @param buffer the buffer to render to
+     * @param menu the menu that is currently rendered
+     * @param depth the current depth in the menu hierarchy
+     */
+    protected void renderMenuList(HtmlStringBuffer buffer, Menu menu, int depth) {
+        buffer.elementStart("ul");
+        renderMenuListAttributes(buffer, menu, depth);
+        buffer.closeTag();
+        buffer.append("\n");
 
-            if (isSelected()) {
-                buffer.appendAttribute("class", "selected");
-            }
+        Iterator it = menu.getChildren().iterator();
+        while (it.hasNext()) {
+            Menu child = (Menu) it.next();
 
-            buffer.closeTag();
+            if (canRender(child, depth)) {
 
-            if (StringUtils.isNotBlank(getImageSrc())) {
-                buffer.elementStart("img");
-                buffer.appendAttribute("border", "0");
-                buffer.appendAttribute("class", "link");
+                buffer.elementStart("li");
+                renderMenuListItemAttributes(buffer, child, depth);
+                buffer.closeTag();
 
-                if (getTitle() != null) {
-                    buffer.appendAttributeEscaped("alt", getTitle());
+                if (menu.isSeparator()) {
+                    renderSeparator(buffer, menu);
                 } else {
-                    buffer.appendAttributeEscaped("alt", getLabel());
+                    renderMenuLink(buffer, child);
                 }
 
-                String src = getImageSrc();
-                if (StringUtils.isNotBlank(src)) {
-                    if (src.charAt(0) == '/') {
-                        src = getContext().getRequest().getContextPath() + src;
-                    }
-                    buffer.appendAttribute("src", src);
+                if (child.hasChildren()) {
+                    buffer.append("\n");
+                    renderMenuList(buffer, child, depth + 1);
                 }
+                buffer.elementEnd("li");
+                buffer.append("\n");
+            }
+        }
 
-                buffer.elementEnd();
+        buffer.elementEnd("ul");
+        buffer.append("\n");
+    }
 
-                if (getLabel() != null) {
-                    buffer.append(getLabel());
-                }
+    /**
+     * Return true if the given menu can be rendered, false otherwise. If the
+     * menu {@link #hasRoles() has roles} defined, this method will return
+     * true if the user is in one of the menu roles, false otherwise.
+     * <p/>
+     * This method delegates to {@link #isUserInRoles()} if the menu has roles
+     * defined.
+     *
+     * @param menu the menu that should be rendered or not
+     * @param depth the current depth in the menu hierarchy
+     * @return true if the menu can be rendered, false otherwise
+     */
+    protected boolean canRender(Menu menu, int depth) {
+        // TODO add and check visible property
+        if (menu.hasRoles()) {
+            return menu.isUserInRoles();
+        } else {
+            return true;
+        }
+    }
 
-            } else {
-                buffer.append(getLabel());
-            }
+    /**
+     * Render the attributes of the menu list (&gt;ul&lt;).
+     *
+     * @param buffer the buffer to render to
+     * @param menu the menu being rendered
+     * @param depth the current depth in the menu hierarchy
+     */
+    protected void renderMenuListAttributes(HtmlStringBuffer buffer, Menu menu,
+        int depth) {
 
-            buffer.elementEnd("a");
+        if (depth == 0) {
+            buffer.appendAttribute("class", "menubar");
+        } else {
+            buffer.appendAttribute("class", "submenu");
         }
     }
 
     /**
-     * Return an HTML anchor tag representation of the menu item. If the menu is
-     * a separator this method will return a HR tag &lt;hr/&gt;.  If the menu
-     * item is selected the anchor tag will be rendered with class="selected"
-     * attribute.
+     * Render the attributes of the menu list item (&gt;li&lt;).
+     *
+     * @param buffer the buffer to render to
+     * @param menu the menu being rendered
+     * @param depth the current depth in the menu hierarchy
+     */
+    protected void renderMenuListItemAttributes(HtmlStringBuffer buffer, Menu menu,
+        int depth) {
+
+        if (depth == 0) {
+            buffer.append(" class=\"menuitem topitem");
+        } else {
+            buffer.append(" class=\"menuitem");
+        }
+        if (menu.hasChildren()) {
+            buffer.append(" has-submenu");
+        }
+        buffer.append("\"");
+    }
+
+    /**
+     * Render an HTML link (&lt;a&gt;) representation of the given menu.
      * <p/>
-     * Note for more fine grained rendering control you should use a Velocity
-     * #macro to render the menu item.
+     * If the menu item is selected the anchor tag will be rendered with
+     * class="selected" attribute.
      *
-     * @see Object#toString()
+     * @param buffer the buffer to render to
+     * @param menu the menu to render
+     */
+    protected void renderMenuLink(HtmlStringBuffer buffer, Menu menu) {
+        buffer.elementStart("a");
+
+        String id = menu.getAttribute("id");
+        if (id != null) {
+            buffer.appendAttribute("id", id);
+        }
+
+        if (menu.getName() != null) {
+            buffer.appendAttribute("name", menu.getName());
+        }
+
+        menu.renderMenuHref(buffer);
+
+        if (menu.getTarget() != null && menu.getTarget().length() > 0) {
+            buffer.appendAttribute("target", menu.getTarget());
+        }
+
+        String menuTitle = menu.getTitle();
+        if (menuTitle != null && menuTitle.length() > 0) {
+            buffer.appendAttributeEscaped("title", menuTitle);
+        }
+
+        if (menu.isSelected()) {
+            buffer.appendAttribute("class", "selected");
+        }
+
+        // TODO need to re-add visible and enabled properties
+        if (menu.hasAttributes()) {
+            buffer.appendAttributes(menu.getAttributes());
+        }
+
+        buffer.closeTag();
+
+        String menuLabel = menu.getLabel();
+
+        if (StringUtils.isNotBlank(menu.getImageSrc())) {
+            buffer.elementStart("img");
+            buffer.appendAttribute("border", "0");
+            buffer.appendAttribute("class", "link");
+
+            if (menuTitle != null) {
+                buffer.appendAttributeEscaped("alt", menuTitle);
+            } else {
+                buffer.appendAttributeEscaped("alt", menuLabel);
+            }
+
+            String src = menu.getImageSrc();
+            if (StringUtils.isNotBlank(src)) {
+                if (src.charAt(0) == '/') {
+                    src = getContext().getRequest().getContextPath() + src;
+                }
+                buffer.appendAttribute("src", src);
+            }
+
+            buffer.elementEnd();
+
+            if (menuLabel != null) {
+                buffer.append(menuLabel);
+            }
+
+        } else {
+            if (menuLabel != null) {
+                buffer.append(menuLabel);
+            }
+        }
+
+        buffer.elementEnd("a");
+    }
+
+    /**
+     * Render an HTML represenatation of the menu as a separator.
      *
-     * @return an HTML anchor tag representation of the menu item
+     * @param buffer the buffer to render to
+     * @param menu the menu to render as a separator
      */
-    @Override
-    public String toString() {
-        HtmlStringBuffer buffer = new HtmlStringBuffer();
-        render(buffer);
-        return buffer.toString();
+    protected void renderSeparator(HtmlStringBuffer buffer, Menu menu) {
+        buffer.append("<hr/>");
     }
 
-    // Protected Methods ------------------------------------------------------
+    /**
+     * Render the menu <tt>"href"</tt> attribute. This method can be overridden
+     * to render dynamic <tt>"href"</tt> parameters, for example:
+     *
+     * <pre class="prettyprint">
+     * public class MyPage extends BorderPage {
+     *
+     *     public MyPage() {
+     *         Menu rootMenu = new MenuFactory().getRootMenu();
+     *
+     *         final String contextPath = getContext().getRequest().getContextPath();
+     *
+     *         Menu menu = new Menu() {
+     *             &#64;Override
+     *             protected void renderMenuHref(HtmlStringBuffer buffer) {
+     *                 buffer.appendAttribute("href", contextPath + "/my-page.htm?customer="
+ getCustomerId());
+     *             }
+     *         });
+     *
+     *         menu.setName("customer");
+     *         menu.setLabel("Customer Lookup");
+     *
+     *         // Guard against adding child menu more than once
+     *         if (!rootMenu.contains(menu)) {
+     *             rootMenu.add(menu);
+     *         }
+     *     }
+     * } </pre>
+     *
+     * @param buffer the buffer to render the href attribute to
+     */
+    protected void renderMenuHref(HtmlStringBuffer buffer) {
+        String href = getHref();
+        buffer.appendAttribute("href", href);
+
+        if ("#".equals(href)) {
+            // If hyperlink does not return false, clicking on it will
+            // scroll to the top of the page.
+            buffer.appendAttribute("onclick", "return false;");
+        }
+    }
 
     /**
      * Return a copy of the Applications root Menu as defined in the



Mime
View raw message