001 002package org.opencms.ui.client.contextmenu; 003 004import org.opencms.ui.shared.CmsContextMenuState.ContextMenuItemState; 005 006import java.util.Set; 007 008import com.google.gwt.core.client.GWT; 009import com.google.gwt.dom.client.Element; 010import com.google.gwt.event.dom.client.KeyCodes; 011import com.google.gwt.event.logical.shared.CloseHandler; 012import com.google.gwt.event.shared.HandlerRegistration; 013import com.google.gwt.user.client.DOM; 014import com.google.gwt.user.client.Event; 015import com.google.gwt.user.client.Event.NativePreviewEvent; 016import com.google.gwt.user.client.Event.NativePreviewHandler; 017import com.google.gwt.user.client.ui.PopupPanel; 018import com.google.gwt.user.client.ui.Widget; 019 020/** 021 * Client side implementation for ContextMenu component.<p> 022 * 023 * Adapted from ContextMenu by Peter Lehto / Vaadin Ltd.<p> 024 */ 025public class CmsContextMenuWidget extends Widget { 026 027 /** The menu overlay. */ 028 private final CmsContextMenuOverlay m_menuOverlay; 029 030 /** The event preview handler. */ 031 private final NativePreviewHandler m_nativeEventHandler = new NativePreviewHandler() { 032 033 @Override 034 public void onPreviewNativeEvent(NativePreviewEvent event) { 035 036 if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { 037 // Always close the context menu on esc, no matter the focus 038 hide(); 039 } 040 041 Event nativeEvent = Event.as(event.getNativeEvent()); 042 boolean targetsContextMenu = eventTargetContextMenu(nativeEvent); 043 044 if (!targetsContextMenu && (nativeEvent.getTypeInt() == Event.ONMOUSEDOWN) && isHideAutomatically()) { 045 hide(); 046 } 047 } 048 }; 049 050 /** The preview handler registration. */ 051 private final HandlerRegistration m_nativeEventHandlerRegistration; 052 053 /** The hide automatically flag. */ 054 private boolean m_hideAutomatically; 055 056 /** The extension target widget. */ 057 private Widget m_extensionTarget; 058 059 /** 060 * Constructor.<p> 061 */ 062 public CmsContextMenuWidget() { 063 064 Element element = DOM.createDiv(); 065 setElement(element); 066 067 m_nativeEventHandlerRegistration = Event.addNativePreviewHandler(m_nativeEventHandler); 068 069 m_menuOverlay = new CmsContextMenuOverlay(); 070 } 071 072 /** 073 * Adds a menu popup close handler.<p> 074 * 075 * @param popupCloseHandler the close handler 076 * 077 * @return the handler registration 078 */ 079 public HandlerRegistration addCloseHandler(CloseHandler<PopupPanel> popupCloseHandler) { 080 081 return m_menuOverlay.addCloseHandler(popupCloseHandler); 082 } 083 084 /** 085 * Adds new item as context menu root item.<p> 086 * 087 * @param rootItem the root item 088 * @param connector the connector 089 */ 090 public void addRootMenuItem(ContextMenuItemState rootItem, CmsContextMenuConnector connector) { 091 092 CmsContextMenuItemWidget itemWidget = createEmptyItemWidget( 093 rootItem.getId(), 094 rootItem.getCaption(), 095 rootItem.getDescription(), 096 connector); 097 itemWidget.setEnabled(rootItem.isEnabled()); 098 itemWidget.setSeparatorVisible(rootItem.isSeparator()); 099 100 setStyleNames(itemWidget, rootItem.getStyles()); 101 102 m_menuOverlay.addMenuItem(itemWidget); 103 104 for (ContextMenuItemState childState : rootItem.getChildren()) { 105 createSubMenu(itemWidget, childState, connector); 106 } 107 } 108 109 /** 110 * Clears the menu items.<p> 111 */ 112 public void clearItems() { 113 114 m_menuOverlay.clearItems(); 115 } 116 117 /** 118 * Returns the extension target widget.<p> 119 * 120 * @return the extension target widget 121 */ 122 public Widget getExtensionTarget() { 123 124 return m_extensionTarget; 125 } 126 127 /** 128 * Hides the menu popup.<p> 129 */ 130 public void hide() { 131 132 m_menuOverlay.hide(); 133 } 134 135 /** 136 * Returns whether the menu is set to hide automatically.<p> 137 * 138 * @return <code>true</code> if the menu is set to hide automatically 139 */ 140 public boolean isHideAutomatically() { 141 142 return m_hideAutomatically; 143 } 144 145 /** 146 * Sets the extension target.<p> 147 * 148 * @param extensionTarget the etension target 149 */ 150 public void setExtensionTarget(Widget extensionTarget) { 151 152 this.m_extensionTarget = extensionTarget; 153 m_menuOverlay.setOwner(extensionTarget); 154 } 155 156 /** 157 * Sets the hide automatically flag.<p> 158 * 159 * @param hideAutomatically the hide automatically flag 160 */ 161 public void setHideAutomatically(boolean hideAutomatically) { 162 163 this.m_hideAutomatically = hideAutomatically; 164 } 165 166 /** 167 * Shows the context menu at the given position.<p> 168 * 169 * @param rootMenuX the client x position 170 * @param rootMenuY the client y position 171 */ 172 public void showContextMenu(int rootMenuX, int rootMenuY) { 173 174 m_menuOverlay.showAt(rootMenuX, rootMenuY); 175 } 176 177 /** 178 * Shows the context menu relative to the given widget.<p> 179 * 180 * @param widget the widget 181 */ 182 public void showContextMenu(Widget widget) { 183 184 m_menuOverlay.showRelativeTo(widget); 185 } 186 187 /** 188 * Unregisters the menu.<p> 189 */ 190 public void unregister() { 191 192 m_nativeEventHandlerRegistration.removeHandler(); 193 m_menuOverlay.unregister(); 194 } 195 196 /** 197 * Returns whether the given event targets the context menu.<p> 198 * 199 * @param nativeEvent the event to check 200 * 201 * @return <code>true</code> if the event targets the menu 202 */ 203 protected boolean eventTargetContextMenu(Event nativeEvent) { 204 205 for (CmsContextMenuItemWidget item : m_menuOverlay.getMenuItems()) { 206 if (item.eventTargetsPopup(nativeEvent)) { 207 return true; 208 } 209 } 210 211 return false; 212 } 213 214 /** 215 * Returns whether the menu is showing.<p> 216 * 217 * @return <code>true</code> if the menu is showing 218 */ 219 protected boolean isShowing() { 220 221 return m_menuOverlay.isShowing(); 222 } 223 224 /** 225 * Creates new empty menu item.<p> 226 * 227 * @param id the id 228 * @param caption the caption 229 * @param description the item description used as tool-tip 230 * @param contextMenuConnector the connector 231 * 232 * @return the menu item 233 */ 234 private CmsContextMenuItemWidget createEmptyItemWidget( 235 String id, 236 String caption, 237 String description, 238 CmsContextMenuConnector contextMenuConnector) { 239 240 CmsContextMenuItemWidget widget = GWT.create(CmsContextMenuItemWidget.class); 241 widget.setId(id); 242 widget.setCaption(caption); 243 widget.setTitle(description); 244 widget.setIcon(contextMenuConnector.getConnection().getIcon(contextMenuConnector.getResourceUrl(id))); 245 246 CmsContextMenuItemWidgetHandler handler = new CmsContextMenuItemWidgetHandler(widget, contextMenuConnector); 247 widget.addClickHandler(handler); 248 widget.addMouseOutHandler(handler); 249 widget.addMouseOverHandler(handler); 250 widget.addKeyUpHandler(handler); 251 widget.setRootComponent(this); 252 253 return widget; 254 } 255 256 /** 257 * Creates a new sub menu.<p> 258 * 259 * @param parentWidget the parent widget 260 * @param childState the child state 261 * @param connector the connector 262 */ 263 private void createSubMenu( 264 CmsContextMenuItemWidget parentWidget, 265 ContextMenuItemState childState, 266 CmsContextMenuConnector connector) { 267 268 CmsContextMenuItemWidget childWidget = createEmptyItemWidget( 269 childState.getId(), 270 childState.getCaption(), 271 childState.getDescription(), 272 connector); 273 childWidget.setEnabled(childState.isEnabled()); 274 childWidget.setSeparatorVisible(childState.isSeparator()); 275 setStyleNames(childWidget, childState.getStyles()); 276 parentWidget.addSubMenuItem(childWidget); 277 278 for (ContextMenuItemState child : childState.getChildren()) { 279 createSubMenu(childWidget, child, connector); 280 } 281 } 282 283 /** 284 * Adds the given style names to the item widget.<p> 285 * 286 * @param item the item 287 * @param styles the style names 288 */ 289 private void setStyleNames(CmsContextMenuItemWidget item, Set<String> styles) { 290 291 for (String style : styles) { 292 item.addStyleName(style); 293 } 294 } 295}