/*******************************************************************************
* Copyright (c) 2012-2016 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.extension.machine.client.actions;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.machine.gwt.client.MachineServiceClient;
import org.eclipse.che.api.machine.gwt.client.events.ExtServerStateEvent;
import org.eclipse.che.api.machine.gwt.client.events.ExtServerStateHandler;
import org.eclipse.che.api.machine.shared.dto.CommandDto;
import org.eclipse.che.api.machine.shared.dto.MachineDto;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.FunctionException;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.workspace.gwt.client.WorkspaceServiceClient;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.action.AbstractPerspectiveAction;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.CustomComponentAction;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.action.Presentation;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.event.project.CloseCurrentProjectEvent;
import org.eclipse.che.ide.api.event.project.CloseCurrentProjectHandler;
import org.eclipse.che.ide.extension.machine.client.MachineLocalizationConstant;
import org.eclipse.che.ide.extension.machine.client.MachineResources;
import org.eclipse.che.ide.extension.machine.client.command.CommandConfiguration;
import org.eclipse.che.ide.extension.machine.client.command.CommandType;
import org.eclipse.che.ide.extension.machine.client.command.CommandTypeRegistry;
import org.eclipse.che.ide.extension.machine.client.command.edit.EditCommandsPresenter;
import org.eclipse.che.ide.ui.dropdown.DropDownHeaderWidget;
import org.eclipse.che.ide.ui.dropdown.DropDownListFactory;
import org.eclipse.che.ide.util.loging.Log;
import org.vectomatic.dom.svg.ui.SVGImage;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import static org.eclipse.che.ide.extension.machine.client.MachineExtension.GROUP_COMMANDS_LIST;
import static org.eclipse.che.ide.workspace.perspectives.project.ProjectPerspective.PROJECT_PERSPECTIVE_ID;
/**
* Action that allows user to select command from list of all commands.
*
* @author Artem Zatsarynnyi
* @author Oleksii Orel
*/
@Singleton
public class SelectCommandComboBoxReady extends AbstractPerspectiveAction implements CustomComponentAction,
EditCommandsPresenter.ConfigurationChangedListener,
CloseCurrentProjectHandler,
ExtServerStateHandler,
DropDownHeaderWidget.ActionDelegate {
public static final String GROUP_COMMANDS = "CommandsGroup";
private static final Comparator<CommandConfiguration> commandsComparator = new CommandsComparator();
private final MachineLocalizationConstant locale;
private final DropDownHeaderWidget dropDownHeaderWidget;
private final HTML devMachineLabelWidget;
private final DropDownListFactory dropDownListFactory;
private final AppContext appContext;
private final String workspaceId;
private final ActionManager actionManager;
private final WorkspaceServiceClient workspaceServiceClient;
private final MachineServiceClient machineServiceClient;
private final CommandTypeRegistry commandTypeRegistry;
private final MachineResources resources;
private List<CommandConfiguration> commands;
private DefaultActionGroup commandActions;
private String lastDevMachineId;
@Inject
public SelectCommandComboBoxReady(MachineLocalizationConstant locale,
MachineResources resources,
ActionManager actionManager,
EventBus eventBus,
DropDownListFactory dropDownListFactory,
WorkspaceServiceClient workspaceServiceClient,
MachineServiceClient machineServiceClient,
CommandTypeRegistry commandTypeRegistry,
EditCommandsPresenter editCommandsPresenter,
AppContext appContext) {
super(Collections.singletonList(PROJECT_PERSPECTIVE_ID),
locale.selectCommandControlTitle(),
locale.selectCommandControlDescription(),
null, null);
this.locale = locale;
this.resources = resources;
this.actionManager = actionManager;
this.workspaceServiceClient = workspaceServiceClient;
this.machineServiceClient = machineServiceClient;
this.commandTypeRegistry = commandTypeRegistry;
this.dropDownListFactory = dropDownListFactory;
this.appContext = appContext;
this.workspaceId = appContext.getWorkspace().getId();
this.dropDownHeaderWidget = dropDownListFactory.createList(GROUP_COMMANDS_LIST);
this.devMachineLabelWidget = new HTML(locale.selectCommandEmptyCurrentDevMachineText());
commands = new LinkedList<>();
eventBus.addHandler(ExtServerStateEvent.TYPE, this);
editCommandsPresenter.addConfigurationsChangedListener(this);
commandActions = new DefaultActionGroup(GROUP_COMMANDS, false, actionManager);
actionManager.registerAction(GROUP_COMMANDS, commandActions);
lastDevMachineId = null;
dropDownHeaderWidget.setDelegate(this);
}
@Override
public void onSelect() {
//do nothing
}
@Override
public void updateInPerspective(@NotNull ActionEvent event) {
final String currentDevMachineId = appContext.getDevMachineId();
if (currentDevMachineId == null) {
return;
}
if (lastDevMachineId == null || !currentDevMachineId.equals(lastDevMachineId)) {
//Gets DevMachine name by ID.
machineServiceClient.getMachine(currentDevMachineId).then(new Operation<MachineDto>() {
@Override
public void apply(MachineDto arg) throws OperationException {
devMachineLabelWidget.setText(arg.getName());
}
});
lastDevMachineId = currentDevMachineId;
}
}
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public Widget createCustomComponent(Presentation presentation) {
// Create widgets for custom component 'select command'.
FlowPanel customComponentHeader = new FlowPanel();
FlowPanel devMachineIconPanel = new FlowPanel();
FlowPanel commandIconPanel = new FlowPanel();
customComponentHeader.setStyleName(resources.getCss().selectCommandBox());
devMachineIconPanel.setStyleName(resources.getCss().selectCommandBoxIconPanel());
devMachineIconPanel.add(new SVGImage(resources.devMachine()));
customComponentHeader.add(devMachineIconPanel);
customComponentHeader.add(this.devMachineLabelWidget);
commandIconPanel.setStyleName(resources.getCss().selectCommandBoxIconPanel());
commandIconPanel.add(new SVGImage(resources.cmdIcon()));
customComponentHeader.add(commandIconPanel);
customComponentHeader.add((Widget)dropDownHeaderWidget);
return customComponentHeader;
}
/** Returns selected command. */
@Nullable
public CommandConfiguration getSelectedCommand() {
if (commands.isEmpty()) {
return null;
}
final String selectedCommandName = dropDownHeaderWidget.getSelectedElementName();
for (CommandConfiguration configuration : commands) {
if (configuration.getName().equals(selectedCommandName)) {
return configuration;
}
}
return null;
}
/** Returns command by it's name. */
@Nullable
public CommandConfiguration getCommandByName(String name) {
if (commands.isEmpty()) {
return null;
}
for (CommandConfiguration configuration : commands) {
if (configuration.getName().equals(name)) {
return configuration;
}
}
return null;
}
public void setSelectedCommand(CommandConfiguration command) {
dropDownHeaderWidget.selectElement(command.getName());
}
@Override
public void onCloseCurrentProject(CloseCurrentProjectEvent event) {
setCommandConfigurations(Collections.<CommandConfiguration>emptyList(), null);
}
/**
* Load all saved commands.
*
* @param commandToSelect
* command that should be selected after loading all commands
*/
private void loadCommands(@Nullable final CommandConfiguration commandToSelect) {
workspaceServiceClient.getCommands(workspaceId).then(new Function<List<CommandDto>, List<CommandConfiguration>>() {
@Override
public List<CommandConfiguration> apply(List<CommandDto> arg) throws FunctionException {
final List<CommandConfiguration> configurationList = new ArrayList<>();
for (CommandDto command : arg) {
final CommandType type = commandTypeRegistry.getCommandTypeById(command.getType());
// skip command if it's type isn't registered
if (type != null) {
try {
configurationList.add(type.getConfigurationFactory().createFromDto(command));
} catch (IllegalArgumentException e) {
Log.warn(EditCommandsPresenter.class, e.getMessage());
}
}
}
return configurationList;
}
}).then(new Operation<List<CommandConfiguration>>() {
@Override
public void apply(List<CommandConfiguration> commandConfigurations) throws OperationException {
setCommandConfigurations(commandConfigurations, commandToSelect);
}
});
}
/**
* Sets command configurations to the list.
*
* @param commandConfigurations
* collection of command configurations to set
* @param commandToSelect
* command that should be selected or {@code null} if none
*/
private void setCommandConfigurations(@NotNull List<CommandConfiguration> commandConfigurations,
@Nullable CommandConfiguration commandToSelect) {
final DefaultActionGroup commandsList = (DefaultActionGroup)actionManager.getAction(GROUP_COMMANDS_LIST);
commands.clear();
clearCommandActions(commandsList);
commandActions.removeAll();
Collections.sort(commandConfigurations, commandsComparator);
CommandConfiguration prevCommand = null;
for (CommandConfiguration configuration : commandConfigurations) {
if (prevCommand == null || !configuration.getType().getId().equals(prevCommand.getType().getId())) {
commandActions.addSeparator(configuration.getType().getDisplayName());
}
commandActions.add(dropDownListFactory.createElement(configuration.getName(), configuration.getName(), dropDownHeaderWidget));
prevCommand = configuration;
}
commandsList.addAll(commandActions);
commands.addAll(commandConfigurations);
if (commandToSelect != null) {
setSelectedCommand(commandToSelect);
} else {
selectLastUsedCommand();
}
}
private void clearCommandActions(@NotNull DefaultActionGroup commandsList) {
for (Action action : commandActions.getChildActionsOrStubs()) {
commandsList.remove(action);
}
}
/** Selects last used command. */
private void selectLastUsedCommand() {
if (commands.isEmpty()) {
setEmptyCommand();
} else {
// TODO: consider to saving last used command ID somewhere
// for now, we always select first command
final CommandConfiguration command = commands.get(0);
dropDownHeaderWidget.selectElement(command.getName());
}
}
/** Clears the selected element in the 'Select Command' menu. */
private void setEmptyCommand() {
dropDownHeaderWidget.selectElement(this.locale.selectCommandEmptyCurrentCommandText());
}
@Override
public void onConfigurationAdded(CommandConfiguration command) {
loadCommands(null);
}
@Override
public void onConfigurationRemoved(CommandConfiguration command) {
loadCommands(null);
}
@Override
public void onConfigurationsUpdated(CommandConfiguration command) {
loadCommands(command);
}
@Override
public void onExtServerStarted(ExtServerStateEvent event) {
loadCommands(null);
}
@Override
public void onExtServerStopped(ExtServerStateEvent event) {
}
private static class CommandsComparator implements Comparator<CommandConfiguration> {
@Override
public int compare(CommandConfiguration o1, CommandConfiguration o2) {
return o1.getType().getId().compareTo(o2.getType().getId());
}
}
}