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 Element element = DOM.createDiv(); 064 setElement(element); 065 066 m_nativeEventHandlerRegistration = Event.addNativePreviewHandler(m_nativeEventHandler); 067 068 m_menuOverlay = new CmsContextMenuOverlay(); 069 } 070 071 /** 072 * Adds a menu popup close handler.<p> 073 * 074 * @param popupCloseHandler the close handler 075 * 076 * @return the handler registration 077 */ 078 public HandlerRegistration addCloseHandler(CloseHandler<PopupPanel> popupCloseHandler) { 079 080 return m_menuOverlay.addCloseHandler(popupCloseHandler); 081 } 082 083 /** 084 * Adds new item as context menu root item.<p> 085 * 086 * @param rootItem the root item 087 * @param connector the connector 088 */ 089 public void addRootMenuItem(ContextMenuItemState rootItem, CmsContextMenuConnector connector) { 090 091 CmsContextMenuItemWidget itemWidget = createEmptyItemWidget( 092 rootItem.getId(), 093 rootItem.getCaption(), 094 rootItem.getDescription(), 095 connector); 096 itemWidget.setEnabled(rootItem.isEnabled()); 097 itemWidget.setSeparatorVisible(rootItem.isSeparator()); 098 099 setStyleNames(itemWidget, rootItem.getStyles()); 100 101 m_menuOverlay.addMenuItem(itemWidget); 102 103 for (ContextMenuItemState childState : rootItem.getChildren()) { 104 createSubMenu(itemWidget, childState, connector); 105 } 106 } 107 108 /** 109 * Clears the menu items.<p> 110 */ 111 public void clearItems() { 112 113 m_menuOverlay.clearItems(); 114 } 115 116 /** 117 * Returns the extension target widget.<p> 118 * 119 * @return the extension target widget 120 */ 121 public Widget getExtensionTarget() { 122 123 return m_extensionTarget; 124 } 125 126 /** 127 * Hides the menu popup.<p> 128 */ 129 public void hide() { 130 131 m_menuOverlay.hide(); 132 } 133 134 /** 135 * Returns whether the menu is set to hide automatically.<p> 136 * 137 * @return <code>true</code> if the menu is set to hide automatically 138 */ 139 public boolean isHideAutomatically() { 140 141 return m_hideAutomatically; 142 } 143 144 /** 145 * Sets the extension target.<p> 146 * 147 * @param extensionTarget the etension target 148 */ 149 public void setExtensionTarget(Widget extensionTarget) { 150 151 this.m_extensionTarget = extensionTarget; 152 m_menuOverlay.setOwner(extensionTarget); 153 } 154 155 /** 156 * Sets the hide automatically flag.<p> 157 * 158 * @param hideAutomatically the hide automatically flag 159 */ 160 public void setHideAutomatically(boolean hideAutomatically) { 161 162 this.m_hideAutomatically = hideAutomatically; 163 } 164 165 /** 166 * Shows the context menu at the given position.<p> 167 * 168 * @param rootMenuX the client x position 169 * @param rootMenuY the client y position 170 */ 171 public void showContextMenu(int rootMenuX, int rootMenuY) { 172 173 m_menuOverlay.showAt(rootMenuX, rootMenuY); 174 } 175 176 /** 177 * Shows the context menu relative to the given widget.<p> 178 * 179 * @param widget the widget 180 */ 181 public void showContextMenu(Widget widget) { 182 183 m_menuOverlay.showRelativeTo(widget); 184 } 185 186 /** 187 * Unregisters the menu.<p> 188 */ 189 public void unregister() { 190 191 m_nativeEventHandlerRegistration.removeHandler(); 192 m_menuOverlay.unregister(); 193 } 194 195 /** 196 * Returns whether the given event targets the context menu.<p> 197 * 198 * @param nativeEvent the event to check 199 * 200 * @return <code>true</code> if the event targets the menu 201 */ 202 protected boolean eventTargetContextMenu(Event nativeEvent) { 203 204 for (CmsContextMenuItemWidget item : m_menuOverlay.getMenuItems()) { 205 if (item.eventTargetsPopup(nativeEvent)) { 206 return true; 207 } 208 } 209 210 return false; 211 } 212 213 /** 214 * Returns whether the menu is showing.<p> 215 * 216 * @return <code>true</code> if the menu is showing 217 */ 218 protected boolean isShowing() { 219 220 return m_menuOverlay.isShowing(); 221 } 222 223 /** 224 * Creates new empty menu item.<p> 225 * 226 * @param id the id 227 * @param caption the caption 228 * @param description the item description used as tool-tip 229 * @param contextMenuConnector the connector 230 * 231 * @return the menu item 232 */ 233 private CmsContextMenuItemWidget createEmptyItemWidget( 234 String id, 235 String caption, 236 String description, 237 CmsContextMenuConnector contextMenuConnector) { 238 239 CmsContextMenuItemWidget widget = GWT.create(CmsContextMenuItemWidget.class); 240 widget.setId(id); 241 widget.setCaption(caption); 242 widget.setTitle(description); 243 widget.setIcon(contextMenuConnector.getConnection().getIcon(contextMenuConnector.getResourceUrl(id))); 244 245 CmsContextMenuItemWidgetHandler handler = new CmsContextMenuItemWidgetHandler(widget, contextMenuConnector); 246 widget.addClickHandler(handler); 247 widget.addMouseOutHandler(handler); 248 widget.addMouseOverHandler(handler); 249 widget.addKeyUpHandler(handler); 250 widget.setRootComponent(this); 251 252 return widget; 253 } 254 255 /** 256 * Creates a new sub menu.<p> 257 * 258 * @param parentWidget the parent widget 259 * @param childState the child state 260 * @param connector the connector 261 */ 262 private void createSubMenu( 263 CmsContextMenuItemWidget parentWidget, 264 ContextMenuItemState childState, 265 CmsContextMenuConnector connector) { 266 267 CmsContextMenuItemWidget childWidget = createEmptyItemWidget( 268 childState.getId(), 269 childState.getCaption(), 270 childState.getDescription(), 271 connector); 272 childWidget.setEnabled(childState.isEnabled()); 273 childWidget.setSeparatorVisible(childState.isSeparator()); 274 setStyleNames(childWidget, childState.getStyles()); 275 parentWidget.addSubMenuItem(childWidget); 276 277 for (ContextMenuItemState child : childState.getChildren()) { 278 createSubMenu(childWidget, child, connector); 279 } 280 } 281 282 /** 283 * Adds the given style names to the item widget.<p> 284 * 285 * @param item the item 286 * @param styles the style names 287 */ 288 private void setStyleNames(CmsContextMenuItemWidget item, Set<String> styles) { 289 290 for (String style : styles) { 291 item.addStyleName(style); 292 } 293 } 294}