/******************************************************************************* * 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.part.widgets.editortab; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ContextMenuEvent; import com.google.gwt.event.dom.client.ContextMenuHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.web.bindery.event.shared.EventBus; import org.eclipse.che.ide.api.editor.EditorAgent; import org.eclipse.che.ide.api.editor.EditorPartPresenter; import org.eclipse.che.ide.api.event.FileEvent; import org.eclipse.che.ide.api.event.FileEvent.FileEventHandler; import org.eclipse.che.ide.api.parts.EditorPartStack; import org.eclipse.che.ide.api.parts.EditorTab; import org.eclipse.che.ide.api.parts.PartPresenter; import org.eclipse.che.ide.api.parts.PartStackUIResources; import org.eclipse.che.ide.api.parts.PartStackView.TabPosition; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceChangedEvent; import org.eclipse.che.ide.api.resources.ResourceChangedEvent.ResourceChangedHandler; import org.eclipse.che.ide.api.resources.ResourceDelta; import org.eclipse.che.ide.api.resources.VirtualFile; import org.eclipse.che.ide.part.editor.EditorTabContextMenuFactory; import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.util.UUID; import org.vectomatic.dom.svg.ui.SVGImage; import org.vectomatic.dom.svg.ui.SVGResource; import javax.validation.constraints.NotNull; import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.CLOSE; import static org.eclipse.che.ide.api.resources.ResourceDelta.ADDED; import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_FROM; import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_TO; import static org.eclipse.che.ide.api.resources.ResourceDelta.UPDATED; /** * Editor tab widget. Contains icon, title and close mark. * May be pinned. Pin state indicates whether this tab should be skipped during operation "Close All but Pinned". * * @author Dmitry Shnurenko * @author Valeriy Svydenko * @author Vitaliy Guliy * @author Vlad Zhukovskyi */ public class EditorTabWidget extends Composite implements EditorTab, ContextMenuHandler, ResourceChangedHandler, FileEventHandler { interface EditorTabWidgetUiBinder extends UiBinder<Widget, EditorTabWidget> { } private static final EditorTabWidgetUiBinder UI_BINDER = GWT.create(EditorTabWidgetUiBinder.class); @UiField SimplePanel iconPanel; @UiField Label title; @UiField FlowPanel closeButton; @UiField(provided = true) final PartStackUIResources resources; private final EditorTabContextMenuFactory editorTabContextMenu; private final String id; private final EditorPartPresenter relatedEditorPart; private final EditorPartStack relatedEditorPartStack; private final EditorAgent editorAgent; private ActionDelegate delegate; private SVGResource icon; private boolean pinned; private VirtualFile file; @Inject public EditorTabWidget(@Assisted final EditorPartPresenter relatedEditorPart, @Assisted EditorPartStack relatedEditorPartStack, PartStackUIResources resources, EditorTabContextMenuFactory editorTabContextMenu, final EventBus eventBus, final EditorAgent editorAgent) { this.resources = resources; this.relatedEditorPart = relatedEditorPart; this.relatedEditorPartStack = relatedEditorPartStack; this.editorAgent = editorAgent; initWidget(UI_BINDER.createAndBindUi(this)); this.editorTabContextMenu = editorTabContextMenu; this.file = relatedEditorPart.getEditorInput().getFile(); this.icon = relatedEditorPart.getTitleImage(); this.title.setText(relatedEditorPart.getTitle()); this.id = title + UUID.uuid(4); iconPanel.add(getIcon()); addDomHandler(this, ClickEvent.getType()); addDomHandler(this, DoubleClickEvent.getType()); addDomHandler(this, ContextMenuEvent.getType()); eventBus.addHandler(ResourceChangedEvent.getType(), this); eventBus.addHandler(FileEvent.TYPE, this); sinkEvents(Event.ONMOUSEDOWN); closeButton.addDomHandler(event -> editorAgent.closeEditor(relatedEditorPart), ClickEvent.getType()); } @Override public void onBrowserEvent(Event event) { if (event.getTypeInt() == Event.ONMOUSEDOWN && event.getButton() == NativeEvent.BUTTON_MIDDLE) { editorAgent.closeEditor(relatedEditorPart); } super.onBrowserEvent(event); } /** {@inheritDoc} */ @Override public Widget getIcon() { return new SVGImage(icon); } /** {@inheritDoc} */ @Override @NotNull public String getTitle() { return title.getText(); } /** {@inheritDoc} */ @NotNull @Override public IsWidget getView() { return this.asWidget(); } /** {@inheritDoc} */ @Override public void update(@NotNull PartPresenter part) { title.setText(part.getTitle()); if (part instanceof EditorPartPresenter) { final EditorPartPresenter editorPartPresenter = (EditorPartPresenter)part; file = editorPartPresenter.getEditorInput().getFile(); icon = editorPartPresenter.getTitleImage(); iconPanel.setWidget(getIcon()); } } /** {@inheritDoc} */ @Override public void select() { getElement().setAttribute("focused", ""); } /** {@inheritDoc} */ @Override public void unSelect() { getElement().removeAttribute("focused"); } /** {@inheritDoc} */ @Override public void setTabPosition(@NotNull TabPosition tabPosition) { throw new UnsupportedOperationException("This method doesn't allow in this class " + getClass()); } /** {@inheritDoc} */ @Override public void setErrorMark(boolean isVisible) { if (isVisible) { title.addStyleName(resources.partStackCss().lineError()); } else { title.removeStyleName(resources.partStackCss().lineError()); } } /** {@inheritDoc} */ @Override public void setWarningMark(boolean isVisible) { if (isVisible) { title.addStyleName(resources.partStackCss().lineWarning()); } else { title.removeStyleName(resources.partStackCss().lineWarning()); } } @Override public String getId() { return id; } /** {@inheritDoc} */ @Override public void onClick(@NotNull ClickEvent event) { if (NativeEvent.BUTTON_LEFT == event.getNativeButton()) { delegate.onTabClicked(this); } } /** {@inheritDoc} */ @Override public void onContextMenu(ContextMenuEvent event) { //construct for each editor tab own context menu, //that will have store information about selected virtual file and pin state at first step //in future maybe we should create another mechanism to associate context menu with initial dto's editorTabContextMenu.newContextMenu(this, relatedEditorPart, relatedEditorPartStack) .show(event.getNativeEvent().getClientX(), event.getNativeEvent().getClientY()); } /** {@inheritDoc} */ @Override public void onDoubleClick(@NotNull DoubleClickEvent event) { delegate.onTabDoubleClicked(this); } /** {@inheritDoc} */ @Override public void setDelegate(ActionDelegate delegate) { this.delegate = delegate; } /** {@inheritDoc} */ @Override public void setReadOnlyMark(boolean isVisible) { if (isVisible) { getElement().setAttribute("readonly", ""); } else { getElement().removeAttribute("readonly"); } } /** {@inheritDoc} */ @Override public VirtualFile getFile() { return file; } @Override public void setFile(VirtualFile file) { this.file = file; } @Override public EditorPartPresenter getRelativeEditorPart() { return relatedEditorPart; } /** {@inheritDoc} */ @Override public void setPinMark(boolean pinned) { this.pinned = pinned; if (pinned) { getElement().setAttribute("pinned", ""); } else { getElement().removeAttribute("pinned"); } } /** {@inheritDoc} */ @Override public boolean isPinned() { return pinned; } @Override public void onResourceChanged(ResourceChangedEvent event) { final ResourceDelta delta = event.getDelta(); if (delta.getKind() == ADDED) { if (!delta.getResource().isFile() || (delta.getFlags() & (MOVED_FROM | MOVED_TO)) == 0) { return; } final Resource resource = delta.getResource(); final Path movedFrom = delta.getFromPath(); if (file.getLocation().equals(movedFrom)) { file = (VirtualFile)resource; title.setText(file.getDisplayName()); } } else if (delta.getKind() == UPDATED) { if (!delta.getResource().isFile()) { return; } final Resource resource = delta.getResource(); if (file.getLocation().equals(resource.getLocation())) { file = (VirtualFile)resource; title.setText(file.getDisplayName()); } } } @Override public void onFileOperation(FileEvent event) { if (event.getOperationType() == CLOSE && this.equals(event.getEditorTab())) { delegate.onTabClose(this); } } }