/*
* Copyright 2003-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.ide.editorTabs.tabfactory.tabs;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import jetbrains.mps.ide.editorTabs.TabColorProvider;
import jetbrains.mps.ide.editorTabs.tabfactory.NodeChangeCallback;
import jetbrains.mps.ide.editorTabs.tabfactory.TabsComponent;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.ide.relations.RelationComparator;
import jetbrains.mps.ide.undo.MPSUndoUtil;
import jetbrains.mps.plugins.relations.RelationDescriptor;
import jetbrains.mps.util.containers.MultiMap;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeReference;
import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* This class expects subclasses to supply single component to serve UI functions. Subclasses shall use {@link #setContent(JComponent)}
* and {@link #removeContent(JComponent)}, not <code>getComponent().add()</code> or <code>getComponent().remove()</code> as this class
* manages layout constraints of the only child itself. Method {@link #getComponent()} is for external consumers or child unrelated activities.
*/
public abstract class BaseTabsComponent implements TabsComponent {
private final NodeChangeCallback myCallback;
private final CreateModeCallback myCreateModeCallback;
protected final SNodeReference myBaseNode;
protected final Collection<RelationDescriptor> myPossibleTabs;
protected final JComponent myEditor;
protected final boolean myShowGrayed;
private final TabColorProvider myColorProvider;
private final Project myProject;
private List<Document> myEditedDocuments = new ArrayList<Document>();
private SNodeReference myLastNode = null;
private JComponent myComponent;
private volatile boolean myDisposed = false;
protected BaseTabsComponent(SNodeReference baseNode, Set<RelationDescriptor> possibleTabs, JComponent editor, NodeChangeCallback callback, boolean showGrayed,
CreateModeCallback createModeCallback, Project project) {
myBaseNode = baseNode;
final ArrayList<RelationDescriptor> tabs = new ArrayList<RelationDescriptor>(possibleTabs);
Collections.sort(tabs, new RelationComparator());
myPossibleTabs = Collections.unmodifiableList(tabs);
myEditor = editor;
myCallback = callback;
myShowGrayed = showGrayed;
TabColorProvider[] extensions = Extensions.getExtensions(TabColorProvider.EP_NAME, project);
myColorProvider = extensions.length > 0 ? extensions[0] : null;
myProject = project;
myCreateModeCallback = createModeCallback;
myComponent = new JPanel(new BorderLayout());
}
@Override
public void dispose() {
myDisposed = true;
}
protected boolean isDisposed() {
return myDisposed;
}
@Override
public final JComponent getComponent() {
return myComponent;
}
protected void setContent(JComponent component) {
myComponent.add(component, BorderLayout.CENTER);
}
protected void removeContent(JComponent component) {
myComponent.remove(component);
}
@Override
public List<Document> getAllEditedDocuments() {
return myEditedDocuments;
}
@Override
public void editNode(SNodeReference node) {
myLastNode = node;
myCallback.changeNode(node);
}
protected final SNodeReference getEditedNode() {
return myLastNode;
}
protected void enterCreateMode(RelationDescriptor tab) {
myLastNode = null;
myCallback.changeNode(null); // this is workaround for update tab header icon
if (myCreateModeCallback != null) {
myCreateModeCallback.create(tab);
}
}
protected TabEditorLayout updateDocumentsAndNodes() {
List<Document> editedDocumentsNew = new ArrayList<Document>();
TabEditorLayout result = new TabEditorLayout();
SNode baseNode = myBaseNode.resolve(getProject().getRepository());
if (baseNode == null) return result;
//see MPS-23013; if the node was just deleted and the command is not yet finished, the ref can still be resolved (unregistered nodes?)
if (baseNode.getModel() == null) return result;
for (RelationDescriptor d : myPossibleTabs) {
MultiMap<SNodeReference, SNodeReference> topToUses = new MultiMap<SNodeReference, SNodeReference>();
for (SNode n : d.getNodes(baseNode)) {
if (n == null || n.getModel() == null/* n.model == null is hack for MPS-21506*/) {
continue;
}
topToUses.putValue(n.getContainingRoot().getReference(), n.getReference());
}
if (topToUses.isEmpty()) continue;
for (SNodeReference top : topToUses.keySet()) {
editedDocumentsNew.add(MPSUndoUtil.getDoc(getProject().getRepository(), top));
result.add(d, top, topToUses.get(top));
}
}
myEditedDocuments = editedDocumentsNew;
return result;
}
public TabColorProvider getColorProvider() {
return myColorProvider;
}
protected final jetbrains.mps.project.Project getProject() {
return ProjectHelper.toMPSProject(myProject);
}
}