/******************************************************************************* * Copyright (c) 2012-2017 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.processes; import elemental.dom.Element; import elemental.dom.Node; import elemental.events.Event; import elemental.events.EventListener; import elemental.html.DivElement; import elemental.html.SpanElement; import com.google.inject.Inject; import org.eclipse.che.api.core.model.machine.MachineConfig; import org.eclipse.che.api.core.model.workspace.Workspace; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.machine.MachineEntity; import org.eclipse.che.ide.api.parts.PartStackUIResources; import org.eclipse.che.ide.machine.MachineResources; import org.eclipse.che.ide.processes.monitoring.MachineMonitors; import org.eclipse.che.ide.terminal.AddTerminalClickHandler; import org.eclipse.che.ide.ui.Tooltip; import org.eclipse.che.ide.ui.tree.NodeRenderer; import org.eclipse.che.ide.ui.tree.TreeNodeElement; import org.eclipse.che.ide.util.dom.Elements; import org.vectomatic.dom.svg.ui.SVGImage; import org.vectomatic.dom.svg.ui.SVGResource; import java.util.HashMap; import java.util.Map; import static org.eclipse.che.api.core.model.workspace.WorkspaceStatus.RUNNING; import static org.eclipse.che.ide.ui.menu.PositionController.HorizontalAlign.MIDDLE; import static org.eclipse.che.ide.ui.menu.PositionController.VerticalAlign.BOTTOM; /** * Renderer for {@ProcessTreeNode} UI presentation. * * @author Anna Shumilova * @author Roman Nikitenko */ public class ProcessTreeRenderer implements NodeRenderer<ProcessTreeNode> { public static final Map<String, String> LABELS = new HashMap<String, String>() { { put("docker", "dkr"); put("development", "dev"); } }; private final MachineResources resources; private final CoreLocalizationConstant locale; private final PartStackUIResources partStackUIResources; private final MachineMonitors machineMonitors; private final AppContext appContext; private AddTerminalClickHandler addTerminalClickHandler; private PreviewSshClickHandler previewSshClickHandler; private StopProcessHandler stopProcessHandler; @Inject public ProcessTreeRenderer(MachineResources resources, CoreLocalizationConstant locale, PartStackUIResources partStackUIResources, MachineMonitors machineMonitors, AppContext appContext) { this.resources = resources; this.locale = locale; this.partStackUIResources = partStackUIResources; this.machineMonitors = machineMonitors; this.appContext = appContext; } @Override public Element getNodeKeyTextContainer(SpanElement treeNodeLabel) { return (Element)treeNodeLabel.getChildNodes().item(1); } @Override public SpanElement renderNodeContents(ProcessTreeNode node) { SpanElement treeNode; switch (node.getType()) { case MACHINE_NODE: treeNode = createMachineElement(node); break; case COMMAND_NODE: treeNode = createCommandElement(node); break; case TERMINAL_NODE: treeNode = createTerminalElement(node); break; default: treeNode = Elements.createSpanElement(); } Elements.addClassName(resources.getCss().processTreeNode(), treeNode); return treeNode; } private DivElement createMachineLabel(String machineCategory) { final DivElement machineLabel = Elements.createDivElement(); if (LABELS.containsKey(machineCategory)) { machineLabel.setTextContent(LABELS.get(machineCategory)); machineLabel.setClassName(resources.getCss().dockerMachineLabel()); return machineLabel; } machineLabel.setTextContent(machineCategory.substring(0, 3)); machineLabel.setClassName(resources.getCss().differentMachineLabel()); return machineLabel; } private SpanElement createMachineElement(final ProcessTreeNode node) { final MachineEntity machine = (MachineEntity)node.getData(); final String machineId = machine.getId(); final MachineConfig machineConfig = machine.getConfig(); final String machineCategory = machineConfig.isDev() ? locale.devMachineCategory() : machineConfig.getType(); SpanElement root = Elements.createSpanElement(); root.appendChild(createMachineLabel(machineCategory)); Element statusElement = Elements.createSpanElement(resources.getCss().machineStatus()); root.appendChild(statusElement); if (node.isRunning()) { statusElement.appendChild(Elements.createDivElement(resources.getCss().machineStatusRunning())); } else { statusElement.appendChild(Elements.createDivElement(resources.getCss().machineStatusPausedLeft())); statusElement.appendChild(Elements.createDivElement(resources.getCss().machineStatusPausedRight())); } Tooltip.create(statusElement, BOTTOM, MIDDLE, locale.viewMachineRunningTooltip()); /*************************************************************************** * * New terminal button * ***************************************************************************/ Workspace workspace = appContext.getWorkspace(); if (workspace != null && RUNNING == workspace.getStatus() && node.hasTerminalAgent()) { SpanElement newTerminalButton = Elements.createSpanElement(resources.getCss().newTerminalButton()); newTerminalButton.appendChild((Node)new SVGImage(resources.addTerminalIcon()).getElement()); root.appendChild(newTerminalButton); Tooltip.create(newTerminalButton, BOTTOM, MIDDLE, locale.viewNewTerminalTooltip()); newTerminalButton.addEventListener(Event.CLICK, new EventListener() { @Override public void handleEvent(Event event) { event.stopPropagation(); event.preventDefault(); if (addTerminalClickHandler != null) { addTerminalClickHandler.onAddTerminalClick(machineId); } } }, true); /** * This listener cancels mouse events on '+' button and prevents the jitter of the selection in the tree. */ EventListener blockMouseListener = new EventListener() { @Override public void handleEvent(Event event) { event.stopPropagation(); event.preventDefault(); } }; /** * Prevent jitter when pressing mouse on '+' button. */ newTerminalButton.addEventListener(Event.MOUSEDOWN, blockMouseListener, true); newTerminalButton.addEventListener(Event.MOUSEUP, blockMouseListener, true); newTerminalButton.addEventListener(Event.CLICK, blockMouseListener, true); newTerminalButton.addEventListener(Event.DBLCLICK, blockMouseListener, true); } /*************************************************************************** * * SSH button * ***************************************************************************/ if (node.isRunning() && node.hasSSHAgent()) { SpanElement sshButton = Elements.createSpanElement(resources.getCss().sshButton()); sshButton.setTextContent("SSH"); root.appendChild(sshButton); sshButton.addEventListener(Event.CLICK, new EventListener() { @Override public void handleEvent(Event event) { if (previewSshClickHandler != null) { previewSshClickHandler.onPreviewSshClick(machineId); } } }, true); Tooltip.create(sshButton, BOTTOM, MIDDLE, locale.connectViaSSH()); } Element monitorsElement = Elements.createSpanElement(resources.getCss().machineMonitors()); root.appendChild(monitorsElement); Node monitorNode = (Node)machineMonitors.getMonitorWidget(machineId, this).getElement(); monitorsElement.appendChild(monitorNode); Element nameElement = Elements.createSpanElement(resources.getCss().nameLabel()); nameElement.setTextContent(machineConfig.getName()); Tooltip.create(nameElement, BOTTOM, MIDDLE, machineConfig.getName()); root.appendChild(nameElement); return root; } private SpanElement createCommandElement(ProcessTreeNode node) { SpanElement root = Elements.createSpanElement(resources.getCss().commandTreeNode()); root.setAttribute("running", "" + node.isRunning()); root.appendChild(createCloseElement(node)); root.appendChild(createStopProcessElement(node)); SVGResource icon = node.getTitleIcon(); if (icon != null) { SpanElement iconElement = Elements.createSpanElement(resources.getCss().processIcon()); root.appendChild(iconElement); DivElement divElement = Elements.createDivElement(resources.getCss().processIconPanel()); iconElement.appendChild(divElement); divElement.appendChild((Node)new SVGImage(icon).getElement()); DivElement badgeElement = Elements.createDivElement(resources.getCss().processBadge()); divElement.appendChild(badgeElement); } Element nameElement = Elements.createSpanElement(); nameElement.setTextContent(node.getName()); Tooltip.create(nameElement, BOTTOM, MIDDLE, node.getName()); root.appendChild(nameElement); Element spanElement = Elements.createSpanElement(); spanElement.setInnerHTML(" "); root.appendChild(spanElement); return root; } private SpanElement createTerminalElement(ProcessTreeNode node) { SpanElement root = Elements.createSpanElement(resources.getCss().commandTreeNode()); root.appendChild(createCloseElement(node)); SVGResource icon = node.getTitleIcon(); if (icon != null) { SpanElement iconElement = Elements.createSpanElement(resources.getCss().processIcon()); root.appendChild(iconElement); DivElement divElement = Elements.createDivElement(resources.getCss().processIconPanel()); iconElement.appendChild(divElement); divElement.appendChild((Node)new SVGImage(icon).getElement()); } Element nameElement = Elements.createSpanElement(); nameElement.setTextContent(node.getName()); Tooltip.create(nameElement, BOTTOM, MIDDLE, node.getName()); root.appendChild(nameElement); Element spanElement = Elements.createSpanElement(); spanElement.setInnerHTML(" "); root.appendChild(spanElement); return root; } private SpanElement createCloseElement(final ProcessTreeNode node) { SpanElement closeButton = Elements.createSpanElement(resources.getCss().processesPanelCloseButtonForProcess()); SVGImage icon = new SVGImage(partStackUIResources.closeIcon()); closeButton.appendChild((Node)icon.getElement()); Tooltip.create(closeButton, BOTTOM, MIDDLE, locale.viewCloseProcessOutputTooltip()); closeButton.addEventListener(Event.CLICK, new EventListener() { @Override public void handleEvent(Event event) { if (stopProcessHandler != null) { stopProcessHandler.onCloseProcessOutputClick(node); } } }, true); return closeButton; } private SpanElement createStopProcessElement(final ProcessTreeNode node) { SpanElement stopProcessButton = Elements.createSpanElement(resources.getCss().processesPanelStopButtonForProcess()); Tooltip.create(stopProcessButton, BOTTOM, MIDDLE, locale.viewStropProcessTooltip()); stopProcessButton.addEventListener(Event.CLICK, new EventListener() { @Override public void handleEvent(Event event) { if (stopProcessHandler != null) { stopProcessHandler.onStopProcessClick(node); } } }, true); return stopProcessButton; } @Override public void updateNodeContents(TreeNodeElement<ProcessTreeNode> treeNode) { } public void setAddTerminalClickHandler(AddTerminalClickHandler addTerminalClickHandler) { this.addTerminalClickHandler = addTerminalClickHandler; } public void setPreviewSshClickHandler(PreviewSshClickHandler previewSshClickHandler) { this.previewSshClickHandler = previewSshClickHandler; } public void setStopProcessHandler(StopProcessHandler stopProcessHandler) { this.stopProcessHandler = stopProcessHandler; } }