/*******************************************************************************
* 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.editor;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.ResizeComposite;
import com.google.gwt.user.client.ui.Widget;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.api.parts.PartPresenter;
import org.eclipse.che.ide.api.parts.PartStackView;
import org.eclipse.che.ide.part.widgets.panemenu.EditorPaneMenu;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static com.google.gwt.dom.client.Style.Display.BLOCK;
import static com.google.gwt.dom.client.Style.Display.NONE;
import static com.google.gwt.dom.client.Style.Unit.PCT;
/**
* @author Evgen Vidolob
* @author Dmitry Shnurenko
* @author Vitaliy Guliy
*/
public class EditorPartStackView extends ResizeComposite implements PartStackView, MouseDownHandler {
interface PartStackUiBinder extends UiBinder<Widget, EditorPartStackView> {
}
private static final PartStackUiBinder UI_BINDER = GWT.create(PartStackUiBinder.class);
@UiField
DockLayoutPanel parent;
@UiField
FlowPanel tabsPanel;
@UiField
DeckLayoutPanel contentPanel;
private final Map<PartPresenter, TabItem> tabs;
private final AcceptsOneWidget partViewContainer;
private final LinkedList<PartPresenter> contents;
private ActionDelegate delegate;
private EditorPaneMenu editorPaneMenu;
private TabItem activeTab;
public EditorPartStackView() {
this.tabs = new HashMap<>();
this.contents = new LinkedList<>();
initWidget(UI_BINDER.createAndBindUi(this));
partViewContainer = new AcceptsOneWidget() {
@Override
public void setWidget(IsWidget widget) {
contentPanel.add(widget);
}
};
addDomHandler(this, MouseDownEvent.getType());
setMaximized(false);
}
/** {@inheritDoc} */
@Override
protected void onAttach() {
super.onAttach();
Style style = getElement().getParentElement().getStyle();
style.setHeight(100, PCT);
style.setWidth(100, PCT);
}
/**
* Adds editor pane menu button in special place on view.
*
* @param editorPaneMenu
* button which will be added
*/
public void addPaneMenuButton(@NotNull EditorPaneMenu editorPaneMenu) {
this.editorPaneMenu = editorPaneMenu;
tabsPanel.add(editorPaneMenu);
}
/** {@inheritDoc} */
@Override
public void onMouseDown(@NotNull MouseDownEvent event) {
delegate.onRequestFocus();
}
/** {@inheritDoc} */
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
/** {@inheritDoc} */
@Override
public void addTab(@NotNull TabItem tabItem, @NotNull PartPresenter partPresenter) {
/** Show editor area if it is empty and hidden */
if (contents.isEmpty()) {
getElement().getParentElement().getStyle().setDisplay(BLOCK);
}
/** Add editor tab to tab panel */
tabsPanel.add(tabItem.getView());
/** Process added editor tab */
tabs.put(partPresenter, tabItem);
contents.add(partPresenter);
partPresenter.go(partViewContainer);
}
/**
* Makes active tab visible.
*/
private void ensureActiveTabVisible() {
if (activeTab == null) {
return;
}
for (int i = 0; i < tabsPanel.getWidgetCount(); i++) {
if (editorPaneMenu != null && editorPaneMenu != tabsPanel.getWidget(i)) {
tabsPanel.getWidget(i).setVisible(true);
}
}
for (int i = 0; i < tabsPanel.getWidgetCount(); i++) {
Widget currentWidget = tabsPanel.getWidget(i);
Widget activeTabWidget = activeTab.getView().asWidget();
if (editorPaneMenu != null && editorPaneMenu == currentWidget) {
continue;
}
if (activeTabWidget.getAbsoluteTop() > tabsPanel.getAbsoluteTop() && activeTabWidget != currentWidget) {
currentWidget.setVisible(false);
}
}
}
/** {@inheritDoc} */
@Override
public void removeTab(@NotNull PartPresenter presenter) {
TabItem tab = tabs.get(presenter);
tabsPanel.remove(tab.getView());
contentPanel.remove(presenter.getView());
tabs.remove(presenter);
contents.remove(presenter);
if (!contents.isEmpty()) {
selectTab(contents.getLast());
}
//this hack need to force redraw dom element to apply correct styles
tabsPanel.getElement().getStyle().setDisplay(NONE);
tabsPanel.getElement().getOffsetHeight();
tabsPanel.getElement().getStyle().setDisplay(BLOCK);
}
/** {@inheritDoc} */
@Override
public void selectTab(@NotNull PartPresenter partPresenter) {
IsWidget view = partPresenter.getView();
// set/remove attribute 'active' for Selenium tests
for (int i = 0; i < contentPanel.getWidgetCount(); i++) {
contentPanel.getWidget(i).getElement().removeAttribute("active");
}
view.asWidget().getElement().setAttribute("active", "");
int viewIndex = contentPanel.getWidgetIndex(view);
if (viewIndex < 0) {
partPresenter.go(partViewContainer);
viewIndex = contentPanel.getWidgetIndex(view);
}
contentPanel.showWidget(viewIndex);
setActiveTab(partPresenter);
if (partPresenter instanceof TextEditor) {
((TextEditor)partPresenter).activate();
}
}
/**
* Switches to specified tab.
*
* @param part tab part
*/
private void setActiveTab(@NotNull PartPresenter part) {
for (TabItem tab : tabs.values()) {
tab.unSelect();
tab.getView().asWidget().getElement().removeAttribute("active");
}
activeTab = tabs.get(part);
activeTab.select();
activeTab.getView().asWidget().getElement().setAttribute("active", "");
delegate.onRequestFocus();
ensureActiveTabVisible();
}
/** {@inheritDoc} */
@Override
public void setTabPositions(List<PartPresenter> partPositions) {
throw new UnsupportedOperationException("The method doesn't allowed in this class " + getClass());
}
/** {@inheritDoc} */
@Override
public void setFocus(boolean focused) {
if (activeTab == null) {
return;
}
if (focused) {
activeTab.select();
} else {
activeTab.unSelect();
}
}
@Override
public void setMaximized(boolean maximized) {
getElement().setAttribute("maximized", Boolean.toString(maximized));
}
/** {@inheritDoc} */
@Override
public void updateTabItem(@NotNull PartPresenter partPresenter) {
TabItem tab = tabs.get(partPresenter);
tab.update(partPresenter);
}
@Override
public void onResize() {
super.onResize();
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
ensureActiveTabVisible();
}
});
}
}