001 002package org.opencms.ui.client.contextmenu; 003 004import com.google.gwt.user.client.DOM; 005import com.google.gwt.user.client.Event; 006import com.google.gwt.user.client.ui.FlowPanel; 007import com.google.gwt.user.client.ui.FocusWidget; 008import com.google.gwt.user.client.ui.Label; 009import com.vaadin.client.ui.Icon; 010 011/** 012 * ContextMenuItemWidget is client side widget that represents one menu item in 013 * context menu.<p> 014 * 015 * Adapted from ContextMenu by Peter Lehto / Vaadin Ltd.<p> 016 */ 017public class CmsContextMenuItemWidget extends FocusWidget { 018 019 /** The icon. */ 020 protected Icon m_icon; 021 022 /** The icon container. */ 023 private final FlowPanel m_iconContainer; 024 025 /** The id. */ 026 private String m_id; 027 028 /** The overlay. */ 029 private CmsContextMenuOverlay m_overlay; 030 031 /** The parent item. */ 032 private CmsContextMenuItemWidget m_parentItem; 033 034 /** The root panel. */ 035 private final FlowPanel m_root; 036 037 /** The root component. */ 038 private CmsContextMenuWidget m_rootComponent; 039 040 /** The sub menu. */ 041 private CmsContextMenuOverlay m_subMenu; 042 043 /** The text label. */ 044 private final Label m_text; 045 046 /** 047 * Constructor.<p> 048 */ 049 @SuppressWarnings("deprecation") 050 public CmsContextMenuItemWidget() { 051 m_root = new FlowPanel(); 052 m_root.setStylePrimaryName("v-context-menu-item-basic"); 053 054 setElement(m_root.getElement()); 055 056 m_root.addStyleName("v-context-submenu"); 057 058 m_iconContainer = new FlowPanel(); 059 m_iconContainer.setStyleName("v-context-menu-item-basic-icon-container"); 060 061 m_text = new Label(); 062 m_text.setStyleName("v-context-menu-item-basic-text"); 063 064 m_root.add(m_iconContainer); 065 m_root.add(m_text); 066 } 067 068 /** 069 * Adds given context menu item into the sub menu of this item.<p> 070 * 071 * @param contextMenuItem the menu item 072 */ 073 public void addSubMenuItem(CmsContextMenuItemWidget contextMenuItem) { 074 075 if (!hasSubMenu()) { 076 m_subMenu = new CmsContextMenuOverlay(); 077 m_subMenu.setOwner(m_rootComponent.getExtensionTarget()); 078 setStylePrimaryName("v-context-menu-item-basic-submenu"); 079 } 080 081 contextMenuItem.setParentItem(this); 082 m_subMenu.addMenuItem(contextMenuItem); 083 } 084 085 /** 086 * Removes all the items from the submenu of this item. If this menu item 087 * does not have a sub menu, this call has no effect.<p> 088 */ 089 public void clearItems() { 090 091 if (hasSubMenu()) { 092 m_subMenu.clearItems(); 093 } 094 } 095 096 /** 097 * Closes the sibling menu.<p> 098 */ 099 public void closeSiblingMenus() { 100 101 m_overlay.closeSubMenus(); 102 } 103 104 /** 105 * Checks whether the given event targets the menu popup.<p> 106 * 107 * @param nativeEvent the event to check 108 * 109 * @return <code>true</code> if given event targets the overlay of this menu item or 110 * overlay of any of this item's child item. 111 */ 112 public boolean eventTargetsPopup(Event nativeEvent) { 113 114 if (m_overlay.eventTargetsPopup(nativeEvent)) { 115 return true; 116 } 117 118 if (hasSubMenu()) { 119 for (CmsContextMenuItemWidget item : m_subMenu.getMenuItems()) { 120 if (item.eventTargetsPopup(nativeEvent)) { 121 return true; 122 } 123 } 124 } 125 126 return false; 127 } 128 129 /** 130 * Returns the id.<p> 131 * 132 * @return the id 133 */ 134 public String getId() { 135 136 return m_id; 137 } 138 139 /** 140 * Returns the parent item.<p> 141 * 142 * @return menu item that opened the menu to which this item belongs 143 */ 144 public CmsContextMenuItemWidget getParentItem() { 145 146 return m_parentItem; 147 } 148 149 /** 150 * Returns if the item has a sub menu.<p> 151 * 152 * @return <code>true</code> if this item has a sub menu 153 */ 154 public boolean hasSubMenu() { 155 156 return (m_subMenu != null) && (m_subMenu.getNumberOfItems() > 0); 157 } 158 159 /** 160 * Hides the sub menu that's been opened from this item.<p> 161 */ 162 public void hideSubMenu() { 163 164 if (hasSubMenu()) { 165 m_subMenu.hide(); 166 removeStyleName("v-context-menu-item-basic-open"); 167 } 168 } 169 170 /** 171 * Returns whether this is the root item.<p> 172 * 173 * @return <code>true</code> if this item is an item in the root menu 174 */ 175 public boolean isRootItem() { 176 177 return m_parentItem == null; 178 } 179 180 /** 181 * Returns if the sub menu is open.<p> 182 * 183 * @return <code>true</code> if this menu has a sub menu and it's open 184 */ 185 public boolean isSubmenuOpen() { 186 187 return hasSubMenu() && m_subMenu.isShowing(); 188 } 189 190 /** 191 * Sets the caption.<p> 192 * 193 * @param caption the caption to set 194 */ 195 public void setCaption(String caption) { 196 197 m_text.setText(caption); 198 } 199 200 /** 201 * @see com.google.gwt.user.client.ui.FocusWidget#setEnabled(boolean) 202 */ 203 @Override 204 public void setEnabled(boolean enabled) { 205 206 super.setEnabled(enabled); 207 if (enabled) { 208 m_root.removeStyleName("v-context-menu-item-disabled"); 209 } else { 210 m_root.addStyleName("v-context-menu-item-disabled"); 211 } 212 } 213 214 /** 215 * @see com.google.gwt.user.client.ui.FocusWidget#setFocus(boolean) 216 */ 217 @Override 218 public void setFocus(boolean focused) { 219 220 if (hasSubMenu()) { 221 m_subMenu.setFocus(false); 222 } 223 224 super.setFocus(focused); 225 226 if (!focused) { 227 DOM.releaseCapture(getElement()); 228 } 229 } 230 231 /** 232 * Sets the icon.<p> 233 * 234 * @param icon the icon 235 */ 236 public void setIcon(Icon icon) { 237 238 if (icon == null) { 239 m_iconContainer.clear(); 240 this.m_icon = null; 241 } else { 242 m_iconContainer.getElement().appendChild(icon.getElement()); 243 this.m_icon = icon; 244 } 245 } 246 247 /** 248 * Sets the id.<p> 249 * 250 * @param id the id 251 */ 252 public void setId(String id) { 253 254 this.m_id = id; 255 } 256 257 /** 258 * Sets the menu component to which this item belongs to.<p> 259 * 260 * @param owner the owner 261 */ 262 public void setOverlay(CmsContextMenuOverlay owner) { 263 264 this.m_overlay = owner; 265 } 266 267 /** 268 * Sets parent item meaning that this item is in the sub menu of given parent item.<p> 269 * 270 * @param parentItem the parent item 271 */ 272 public void setParentItem(CmsContextMenuItemWidget parentItem) { 273 274 this.m_parentItem = parentItem; 275 } 276 277 /** 278 * Sets the root component.<p> 279 * 280 * @param rootComponent the root component 281 */ 282 public void setRootComponent(CmsContextMenuWidget rootComponent) { 283 284 this.m_rootComponent = rootComponent; 285 } 286 287 /** 288 * Sets the separator visibility.<p> 289 * 290 * @param separatorVisible <code>true</code> to set the separator visible 291 */ 292 public void setSeparatorVisible(boolean separatorVisible) { 293 294 if (separatorVisible) { 295 m_root.addStyleName("v-context-menu-item-separator"); 296 } else { 297 m_root.removeStyleName("v-context-menu-item-separator"); 298 } 299 } 300 301 /** 302 * Closes this item and selects the parent.<p> 303 */ 304 protected void closeThisAndSelectParent() { 305 306 if (!isRootItem()) { 307 setFocus(false); 308 m_parentItem.hideSubMenu(); 309 m_parentItem.setFocus(true); 310 } 311 } 312 313 /** 314 * Called when context menu item is clicked or is focused and enter is 315 * pressed.<p> 316 * 317 * @return <code>true</code> if context menu was closed after the click 318 */ 319 protected boolean onItemClicked() { 320 321 if (isEnabled()) { 322 m_overlay.closeSubMenus(); 323 324 if (hasSubMenu()) { 325 openSubMenu(); 326 return false; 327 } else { 328 if (m_rootComponent.isHideAutomatically()) { 329 closeContextMenu(); 330 return true; 331 } 332 return false; 333 } 334 } 335 336 return false; 337 } 338 339 /** 340 * Selects the next sibling.<p> 341 */ 342 protected void selectLowerSibling() { 343 344 setFocus(false); 345 m_overlay.selectItemAfter(CmsContextMenuItemWidget.this); 346 347 } 348 349 /** 350 * Selects the previous sibling.<p> 351 */ 352 protected void selectUpperSibling() { 353 354 setFocus(false); 355 m_overlay.selectItemBefore(CmsContextMenuItemWidget.this); 356 } 357 358 /** 359 * Closes the menu.<p> 360 */ 361 private void closeContextMenu() { 362 363 if (isRootItem()) { 364 m_rootComponent.hide(); 365 } else { 366 m_parentItem.closeContextMenu(); 367 } 368 } 369 370 /** 371 * Programmatically opens the sub menu of this item.<p> 372 */ 373 private void openSubMenu() { 374 375 if (isEnabled() && hasSubMenu() && !m_subMenu.isShowing()) { 376 m_overlay.closeSubMenus(); 377 378 setFocus(false); 379 addStyleName("v-context-menu-item-basic-open"); 380 m_subMenu.openNextTo(this); 381 } 382 } 383}