View Javadoc
1   /*
2    * Copyright 2000-2018 Vaadin Ltd.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  
17  package com.vaadin.contextmenu.client;
18  
19  import java.util.Iterator;
20  import java.util.Stack;
21  
22  import com.google.gwt.core.client.GWT;
23  import com.google.gwt.dom.client.Element;
24  import com.google.gwt.dom.client.EventTarget;
25  import com.google.gwt.user.client.Command;
26  import com.google.gwt.user.client.Event;
27  import com.google.gwt.user.client.ui.Widget;
28  import com.vaadin.client.HasWidget;
29  import com.vaadin.client.ServerConnector;
30  import com.vaadin.client.communication.StateChangeEvent;
31  import com.vaadin.client.extensions.AbstractExtensionConnector;
32  import com.vaadin.client.ui.VMenuBar;
33  import com.vaadin.contextmenu.ContextMenu;
34  import com.vaadin.shared.ui.ComponentStateUtil;
35  import com.vaadin.shared.ui.Connect;
36  
37  @Connect(ContextMenu.class)
38  public class ContextMenuConnector extends AbstractExtensionConnector
39          implements HasWidget {
40  
41      private VContextMenu contextMenu;
42      private VMenuBar.CustomMenuItem contextMenuRoot;
43  
44      @Override
45      protected void extend(ServerConnector target) {
46  
47      }
48  
49      @Override
50      public void onStateChanged(StateChangeEvent event) {
51          super.onStateChanged(event);
52          contextMenu.client = getConnection();
53          updateFromState(getState());
54      }
55  
56      protected void updateFromState(ContextMenuState state) {
57  
58          contextMenu.enabled = state.enabled;
59          contextMenu.htmlContentAllowed = state.htmlContentAllowed;
60  
61          Stack<Iterator<ContextMenuItemState>> iteratorStack = new Stack<>();
62          Stack<VMenuBar> menuStack = new Stack<>();
63          VMenuBar currentMenu = contextMenuRoot.getSubMenu();
64          currentMenu.clearItems();
65          if (state.menuItems != null && !state.menuItems.isEmpty()) {
66              Iterator<ContextMenuItemState> itr = state.menuItems.iterator();
67              while (itr.hasNext()) {
68                  ContextMenuItemState menuItemState = itr.next();
69                  VMenuBar.CustomMenuItem currentItem;
70  
71                  boolean itemHasCommand = menuItemState.command;
72                  boolean itemIsCheckable = menuItemState.checkable;
73  
74                  String iconUrl = menuItemState.icon == null ? null
75                          : menuItemState.icon.getURL();
76                  boolean subMenu = menuItemState.childItems != null
77                          && !menuItemState.childItems.isEmpty();
78                  String itemHTML = contextMenu.buildItemHTML(
79                          menuItemState.separator, subMenu, iconUrl,
80                          menuItemState.text);
81  
82                  Command cmd = null;
83                  if (!menuItemState.separator) {
84                      if (itemHasCommand || itemIsCheckable) {
85                          // Construct a command that fires onMenuClick(int) with
86                          // the
87                          // item's id-number
88                          cmd = () -> contextMenu.onMenuClick(menuItemState.id);
89                      }
90                  }
91  
92                  currentItem = currentMenu.addItem(itemHTML, cmd);
93                  currentItem.setId("" + menuItemState.id);
94                  updateItemFromState(currentItem, menuItemState);
95  
96                  if (subMenu) {
97                      menuStack.push(currentMenu);
98                      iteratorStack.push(itr);
99                      itr = menuItemState.childItems.iterator();
100                     currentMenu = new VContextMenu(true, currentMenu);
101                     getConnection().getVTooltip()
102                             .connectHandlersToWidget(currentMenu);
103                     // this is the top-level style that also propagates to items
104                     // -
105                     // any item specific styles are set above in
106                     // currentItem.updateFromUIDL(item, client)
107                     if (ComponentStateUtil.hasStyles(getState())) {
108                         for (String style : getState().styles) {
109                             currentMenu.addStyleDependentName(style);
110                         }
111                     }
112                     currentItem.setSubMenu(currentMenu);
113                 }
114 
115                 while (!itr.hasNext() && !iteratorStack.empty()) {
116                     boolean hasCheckableItem = false;
117                     for (VMenuBar.CustomMenuItem menuItem : currentMenu
118                             .getItems()) {
119                         hasCheckableItem = hasCheckableItem
120                                 || menuItem.isCheckable();
121                     }
122                     if (hasCheckableItem) {
123                         currentMenu.addStyleDependentName("check-column");
124                     } else {
125                         currentMenu.removeStyleDependentName("check-column");
126                     }
127 
128                     itr = iteratorStack.pop();
129                     currentMenu = menuStack.pop();
130                 }
131             }
132         }
133     }
134 
135     private void updateItemFromState(VMenuBar.CustomMenuItem currentItem,
136             ContextMenuItemState menuItemState) {
137         currentItem.setSeparator(menuItemState.separator);
138         currentItem.setEnabled(menuItemState.enabled);
139 
140         if (!menuItemState.separator && menuItemState.checked) {
141             // if the selected attribute is present (either true or false),
142             // the item is selectable
143             currentItem.setCheckable(true);
144             currentItem.setChecked(menuItemState.checked);
145         } else {
146             currentItem.setCheckable(false);
147         }
148 
149         currentItem.setStyleName(menuItemState.styleName);
150 
151         currentItem.setDescription(menuItemState.description);
152         currentItem.setDescriptionContentMode(
153                 menuItemState.descriptionContentMode);
154         if (menuItemState.description != null) {
155             currentItem.getElement().setAttribute("title",
156                     menuItemState.description);
157         }
158 
159         currentItem.updateStyleNames();
160 
161     }
162 
163     @Override
164     protected void init() {
165         super.init();
166         contextMenu = GWT.create(VContextMenu.class);
167         contextMenu.connector = this;
168         contextMenuRoot = contextMenu.addItem("", null);
169         contextMenuRoot.setSubMenu(new VContextMenu(true, contextMenu));
170 
171         registerRpc(ContextMenuClientRpc.class, new ContextMenuClientRpc() {
172             @Override
173             public void showContextMenu(int x, int y) {
174                 contextMenu.showRootMenu(x, y);
175             }
176         });
177 
178         Event.addNativePreviewHandler(new Event.NativePreviewHandler() {
179             @Override
180             public void onPreviewNativeEvent(Event.NativePreviewEvent event) {
181                 if (event.getTypeInt() == Event.ONMOUSEWHEEL && contextMenu.isVisible()) {
182                     EventTarget eventTarget = event.getNativeEvent().getEventTarget();
183                     if (contextMenu.getRoot().getMenuItemWithElement(Element.as(eventTarget)) == null) {
184                         contextMenu.hideParents(true);
185                     }
186                 }
187             }
188         });
189 
190     }
191 
192     @Override
193     public ContextMenuState getState() {
194         return (ContextMenuState) super.getState();
195     }
196 
197     public void onMenuClick(int clickedItemId) {
198         getRpcProxy(ContextMenuServerRpc.class).itemClicked(clickedItemId);
199     }
200 
201     @Override
202     public Widget getWidget() {
203         return contextMenuRoot.getSubMenu();
204     }
205 }