package org.vaadin.peter.contextmenu.client.ui; import java.util.LinkedList; import java.util.List; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ui.VOverlay; /** * Menu is a visible item of ContextMenu component. For every child menu new * instance of Menu class will be instantiated. * * @author Peter Lehto / IT Mill Oy Ltd */ class VMenu extends VOverlay { public static final String CLASSNAME = "v-ctxmenu"; private final VContextMenu app; private final VMenu parent; private final FlowPanel root; private Timer openTimer; private boolean immediateOpen; private final List<VMenuItem> menuItems; public VMenu(VContextMenu app, VMenu parentMenu) { super(true, false, true); this.app = app; this.parent = parentMenu; setStyleName("v-ctxmenu-container"); root = new FlowPanel(); root.setStyleName(CLASSNAME); menuItems = new LinkedList<VMenuItem>(); add(root); if (app.hasDefinedWidth()) { this.setWidth(app.getDefinedWidth() + "px"); } } @Override protected com.google.gwt.user.client.Element getStyleElement() { if (this.root == null) { return super.getStyleElement(); } return root.getElement(); } public VMenuItem addMenuItem(int itemId, String name, String styleName, boolean enabled, String iconUri, boolean separator) { VMenuItem item = new VMenuItem(this, itemId, name, styleName, enabled, iconUri, separator); this.menuItems.add(item); this.root.add(item); return item; } @Override public void hide() { super.hide(); for (VMenuItem child : menuItems) { child.hideSubMenu(); } } public List<VMenuItem> getMenuItems() { return this.menuItems; } /** * @return number of visible items in this menu */ public int getNumberOfItems() { return this.menuItems.size(); } public boolean hasParent() { return this.parent != null; } public VMenu getParentMenu() { return this.parent; } /** * ContextMenuItem is a clickable label that represents one option in a menu * component. * * @author Peter Lehto / IT Mill Oy Ltd */ class VMenuItem extends Widget { private final VMenu owner; private final int itemId; private final boolean enabled; private VMenu subMenu; private final boolean separator; public VMenuItem(VMenu owner, int itemId, String name, String styleName, boolean enabled, String iconUri, boolean separator) { this.owner = owner; this.itemId = itemId; this.enabled = enabled; this.separator = separator; sinkEvents(Event.ONCLICK); sinkEvents(Event.MOUSEEVENTS); Element menuItem = DOM.createDiv(); if (separator) { menuItem.setClassName("ctxmenu-menu-item separator"); } else { menuItem.setClassName("ctxmenu-menu-item"); } setElement(menuItem); Element iconContainer = DOM.createDiv(); iconContainer.setClassName("ctxmenu-icon-holder"); Element text = DOM.createDiv(); text.setClassName("ctxmenu-name"); text.setInnerText(name); menuItem.appendChild(iconContainer); menuItem.appendChild(text); if (styleName != null && !styleName.equals("")) { addStyleName(styleName); } if (!enabled) { addStyleName("v-disabled"); } if (iconUri != null) { Element icon = DOM.createImg(); icon.setClassName("ctxmenu-icon"); DOM.setElementProperty(icon, "src", iconUri); iconContainer.appendChild(icon); } } /** * @return item id of this item */ public int getItemId() { return this.itemId; } /** * Hides the sub menu that was opened by this item */ public void hideSubMenu() { if (this.hasSubMenu()) { subMenu.hide(); } } /** * Sets given menu as this menu item's sub menu * * @param subMenu */ public void setSubMenu(VMenu subMenu) { this.subMenu = subMenu; this.addStyleName("ctxmenu-submenu"); } /** * @return sub menu that will be opened when this item is clicked */ public VMenu getSubMenu() { return this.subMenu; } /** * @return true if this menu item has a sub menu, false otherwise */ public boolean hasSubMenu() { return this.subMenu != null; } /** * @return true if this item has a separator */ public boolean hasSeparator() { return this.separator; } @Override public void onBrowserEvent(Event event) { event.preventDefault(); switch (event.getTypeInt()) { case Event.ONCLICK: { if (enabled) { if (openTimer != null) { openTimer.cancel(); immediateOpen = false; } app.itemClicked(this); } break; } case Event.ONMOUSEOVER: { // automatically open submenus after one second of hovering if (enabled && this.hasSubMenu() && immediateOpen == false) { openTimer = new Timer() { @Override public void run() { app.itemClicked(VMenuItem.this); immediateOpen = true; } }; openTimer.schedule(1000); } // open other submenus from same menu immediately when hovered // if user has already hovered another item in the same menu if (enabled && immediateOpen && this.hasSubMenu()) { app.itemClicked(this); } break; } case Event.ONMOUSEOUT: { if (openTimer != null) { openTimer.cancel(); } break; } } } /** * @return menu that owns this menu item */ VMenu getOwener() { return this.owner; } /** * @return List of sibling sub menus of this menu item. */ List<VMenu> getSiblingSubMenus() { List<VMenu> siblingSubMenus = new LinkedList<VMenu>(); for (VMenuItem item : this.getOwener().getMenuItems()) { if (item != this) { if (item.hasSubMenu()) { siblingSubMenus.add(item.getSubMenu()); } } } return siblingSubMenus; } } }