/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.menu;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ActionGroup;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.ActionPlaces;
import org.eclipse.che.ide.api.action.CustomComponentAction;
import org.eclipse.che.ide.api.action.IdeActions;
import org.eclipse.che.ide.api.action.Presentation;
import org.eclipse.che.ide.api.action.Separator;
import org.eclipse.che.ide.api.keybinding.KeyBindingAgent;
import org.eclipse.che.ide.toolbar.ActionSelectedHandler;
import org.eclipse.che.ide.toolbar.CloseMenuHandler;
import org.eclipse.che.ide.toolbar.PresentationFactory;
import org.eclipse.che.ide.toolbar.MenuLockLayer;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Implements {@link MainMenuView} using standard GWT Menu Widgets
*
* @author Nikolay Zamosenchuk
* @author Oleksii Orel
*/
public class MainMenuViewImpl extends Composite implements MainMenuView, CloseMenuHandler, ActionSelectedHandler {
private final MenuResources resources;
private final MenuItemPresentationFactory presentationFactory = new MenuItemPresentationFactory();
/** Working table, cells of which are contains element of Menu. */
private final MenuBarTable table = new MenuBarTable();
private final FlowPanel rightPanel = new FlowPanel();
private final FlowPanel leftPanel = new FlowPanel();
/** Panel, which contains top menu. */
private final FlowPanel rootPanel = new FlowPanel();
/** Lock layer for displaying popup menus. */
private MenuLockLayer lockLayer;
/** List Menu Bar items. */
private Map<Element, MenuBarItem> menuBarItems = new LinkedHashMap<>();
private Map<Action, MenuBarItem> action2barItem = new HashMap<>();
/** Store selected Menu Bar item. */
private MenuBarItem selectedMenuBarItem;
private List<Action> leftVisibleActions = new ArrayList<>();
private List<Action> menuVisibleActions = new ArrayList<>();
private List<Action> rightVisibleActions = new ArrayList<>();
private List<Action> newLeftVisibleActions;
private List<Action> newMenuVisibleActions;
private List<Action> newRightVisibleActions;
private ActionManager actionManager;
private KeyBindingAgent keyBindingAgent;
/** Create new {@link MainMenuViewImpl} */
@Inject
public MainMenuViewImpl(MenuResources resources, ActionManager actionManager, KeyBindingAgent keyBindingAgent) {
this.resources = resources;
this.actionManager = actionManager;
this.keyBindingAgent = keyBindingAgent;
initWidget(rootPanel);
rootPanel.setStyleName(resources.menuCss().menuBar());
leftPanel.addStyleName(resources.menuCss().leftPanel());
rootPanel.add(leftPanel);
table.setStyleName(resources.menuCss().menuBarTable());
table.setCellPadding(0);
table.setCellSpacing(0);
rootPanel.add(table);
rightPanel.addStyleName(resources.menuCss().rightPanel());
rootPanel.add(rightPanel);
}
@Override
public void setDelegate(ActionDelegate delegate) {
new Timer() {
@Override
public void run() {
updateMenuActions();
}
}.scheduleRepeating(1000);
}
/** Handle closing of all popup windows. */
public void onCloseMenu() {
selectedMenuBarItem.setNormalState();
selectedMenuBarItem = null;
lockLayer = null;
}
void updateMenuActions() {
if (selectedMenuBarItem != null) {
return;
}
newMenuVisibleActions = new ArrayList<>();
expandActionGroup(IdeActions.GROUP_MAIN_MENU, newMenuVisibleActions, actionManager);
if (!newMenuVisibleActions.equals(menuVisibleActions)) {
removeAll();
for (final Action action : newMenuVisibleActions) {
add(ActionPlaces.MAIN_MENU, action, presentationFactory);
}
menuVisibleActions = newMenuVisibleActions;
}
newRightVisibleActions = new ArrayList<>();
expandActionGroup(IdeActions.GROUP_RIGHT_MAIN_MENU, newRightVisibleActions, actionManager);
if (!newRightVisibleActions.equals(rightVisibleActions)) {
rightPanel.clear();
for (Action action : newRightVisibleActions) {
addToPanel(rightPanel, action, presentationFactory);
}
rightVisibleActions = newRightVisibleActions;
}
newLeftVisibleActions = new ArrayList<>();
expandActionGroup(IdeActions.GROUP_LEFT_MAIN_MENU, newLeftVisibleActions, actionManager);
if (!newLeftVisibleActions.equals(leftVisibleActions)) {
leftPanel.clear();
for (Action action : newLeftVisibleActions) {
addToPanel(leftPanel, action, presentationFactory);
}
leftVisibleActions = newLeftVisibleActions;
}
}
/**
* Create a new widget and add it to panel menu.
*/
private void addToPanel(FlowPanel panel, Action action, PresentationFactory presentationFactory) {
Presentation presentation = presentationFactory.getPresentation(action);
if (action instanceof Separator) {
panel.add(new SeparatorItem(resources.menuCss().panelSeparator()));
// todo find way to render non custom actions
} else if (action instanceof CustomComponentAction) {
CustomComponentAction customComponentAction = (CustomComponentAction)action;
Widget component = customComponentAction.createCustomComponent(presentation);
component.addStyleName(resources.menuCss().customComponent());
panel.add(component);
}
}
private void removeAll() {
table.clear();
menuBarItems.clear();
action2barItem.clear();
}
private void expandActionGroup(String actionGroupId, final List<Action> newVisibleActions, ActionManager actionManager) {
final ActionGroup mainActionGroup = (ActionGroup)actionManager.getAction(actionGroupId);
if (mainActionGroup == null) return;
expandActionGroup(newVisibleActions, actionManager, mainActionGroup);
}
private void expandActionGroup(List<Action> newVisibleActions, ActionManager actionManager, ActionGroup mainActionGroup) {
final Action[] children = mainActionGroup.getChildren(null);
for (final Action action : children) {
final Presentation presentation = presentationFactory.getPresentation(action);
final ActionEvent e = new ActionEvent(ActionPlaces.MAIN_MENU, presentation, actionManager, 0);
action.update(e);
if (presentation.isVisible()) { // add only visible items
newVisibleActions.add(action);
}
if (action2barItem.containsKey(action)) {
action2barItem.get(action).update();
}
}
}
/**
* Create and add new item in menu.
*/
private void add(String place, Action action, MenuItemPresentationFactory presentationFactory) {
Presentation presentation = presentationFactory.getPresentation(action);
if (action instanceof ActionGroup) {
ActionGroup group = (ActionGroup)action;
table.setText(0, menuBarItems.size(), presentation.getText());
Element element = table.getCellFormatter().getElement(0, menuBarItems.size());
MenuBarItem item =
new MenuBarItem(group, actionManager, presentationFactory, place, element, this, keyBindingAgent, resources.menuCss());
item.onMouseOut();
menuBarItems.put(element, item);
action2barItem.put(group, item);
} else if (action instanceof CustomComponentAction) {
Widget widget = ((CustomComponentAction)action).createCustomComponent(presentation);
table.setWidget(0, menuBarItems.size(), widget);
Element element = table.getCellFormatter().getElement(0, menuBarItems.size());
menuBarItems.put(element, null);
}
}
/**
* Open Popup Menu.
*
* @param item
* popup menu item.
*/
public void openPopupMenu(MenuBarItem item) {
if (lockLayer == null) {
int top = getAbsoluteTop() + getOffsetHeight();
lockLayer = new MenuLockLayer(this, top);
}
item.openPopupMenu(lockLayer);
}
@Override
public void onActionSelected(Action action) {
if (action2barItem.containsKey(action)) {
MenuBarItem item = action2barItem.get(action);
if (selectedMenuBarItem != null && selectedMenuBarItem != item) {
selectedMenuBarItem.setNormalState();
selectedMenuBarItem.closePopupMenu();
}
selectedMenuBarItem = item;
} else {
lockLayer.close();
lockLayer = null;
}
}
/**
* This is visual component.
* Uses for handling mouse events on MenuBar.
*/
private class MenuBarTable extends FlexTable {
public MenuBarTable() {
sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEDOWN);
}
@Override
public void onBrowserEvent(Event event) {
Element td = getEventTargetCell(event);
MenuBarItem item = menuBarItems.get(td);
if (item == null) {
return;
}
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEOVER:
if (selectedMenuBarItem != null && item != selectedMenuBarItem) {
if (item.onMouseDown()) {
openPopupMenu(item);
}
}
item.onMouseOver();
break;
case Event.ONMOUSEOUT:
item.onMouseOut();
break;
case Event.ONMOUSEDOWN:
if (item == selectedMenuBarItem) {
if (lockLayer != null) {
lockLayer.close();
}
return;
}
if (item.onMouseDown()) {
openPopupMenu(item);
}
break;
default:
break;
}
}
}
private static class SeparatorItem extends Composite {
public SeparatorItem(String styleName) {
final FlowPanel widget = new FlowPanel();
widget.addStyleName(styleName);
Element separator = widget.getElement();
for (int i = 0; i < 6; i++) {
separator.appendChild(DOM.createDiv());
}
initWidget(widget);
}
}
}