/*
* Copyright 2000-2009 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 com.intellij.openapi.roots.ui.configuration;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.roots.ui.configuration.artifacts.ArtifactsStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.*;
import com.intellij.openapi.ui.MasterDetailsComponent;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.ui.navigation.History;
import com.intellij.ui.navigation.Place;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import consulo.roots.ui.configuration.ProjectStructureDialog;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class ProjectStructureConfigurable implements SearchableConfigurable, Configurable.HoldPreferredFocusedComponent, Place.Navigator {
public static final DataKey<ProjectStructureConfigurable> KEY = DataKey.create("ProjectStructureConfiguration");
protected final UIState myUiState = new UIState();
@NonNls
public static final String CATEGORY = "category";
public static class UIState {
public String lastEditedConfigurable;
}
private final Project myProject;
private final ArtifactsStructureConfigurable myArtifactsStructureConfigurable;
private History myHistory = new History(this);
private Configurable mySelectedConfigurable;
private final ProjectSdksModel myProjectSdksModel = new ProjectSdksModel();
private ProjectConfigurable myProjectConfig;
private final ProjectLibrariesConfigurable myProjectLibrariesConfig;
private ModuleStructureConfigurable myModulesConfig;
private final List<Configurable> myName2Config = new ArrayList<Configurable>();
private final StructureConfigurableContext myContext;
private final ModulesConfigurator myModuleConfigurator;
private SdkListConfigurable mySdkListConfigurable;
private ProjectStructureDialog myProjectStructureDialog;
public ProjectStructureConfigurable(final Project project,
final ProjectLibrariesConfigurable projectLibrariesConfigurable,
final ModuleStructureConfigurable moduleStructureConfigurable,
ArtifactsStructureConfigurable artifactsStructureConfigurable) {
myProject = project;
myArtifactsStructureConfigurable = artifactsStructureConfigurable;
myModuleConfigurator = new ModulesConfigurator(myProject);
myContext = new StructureConfigurableContext(myProject, myModuleConfigurator);
myModuleConfigurator.setContext(myContext);
myProjectLibrariesConfig = projectLibrariesConfigurable;
myModulesConfig = moduleStructureConfigurable;
myProjectLibrariesConfig.init(myContext);
myModulesConfig.init(myContext);
if (!project.isDefault()) {
myArtifactsStructureConfigurable.init(myContext, myModulesConfig, myProjectLibrariesConfig);
}
myProjectConfig = new ProjectConfigurable(project, getContext(), getModuleConfigurator());
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(myProject);
myUiState.lastEditedConfigurable = propertiesComponent.getValue("project.structure.last.edited");
}
public void setProjectStructureDialog(ProjectStructureDialog projectStructureDialog) {
myProjectStructureDialog = projectStructureDialog;
}
public ModulesConfigurator getModuleConfigurator() {
return myModuleConfigurator;
}
@Override
@NotNull
@NonNls
public String getId() {
return "project.structure";
}
@Override
@Nullable
public Runnable enableSearch(final String option) {
return null;
}
@Override
@Nls
public String getDisplayName() {
return ProjectBundle.message("project.settings.display.name");
}
@Override
@Nullable
@NonNls
public String getHelpTopic() {
return mySelectedConfigurable != null ? mySelectedConfigurable.getHelpTopic() : "";
}
@Override
public JComponent createComponent() {
throw new UnsupportedOperationException();
}
@Override
public boolean isModified() {
for (Configurable each : myName2Config) {
if (each.isModified()) return true;
}
return false;
}
@Override
public void apply() throws ConfigurationException {
for (Configurable each : myName2Config) {
if (each instanceof BaseStructureConfigurable && each.isModified()) {
((BaseStructureConfigurable)each).checkCanApply();
}
}
for (Configurable each : myName2Config) {
if (each.isModified()) {
each.apply();
}
}
myContext.getDaemonAnalyzer().clearCaches();
}
@Override
public void reset() {
// need this to ensure VFS operations will not block because of storage flushing
// and other maintenance IO tasks run in background
AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("Resetting Project Structure");
try {
myContext.reset();
myProjectSdksModel.reset();
Configurable toSelect = null;
for (Configurable each : myName2Config) {
if (myUiState.lastEditedConfigurable != null && myUiState.lastEditedConfigurable.equals(each.getDisplayName())) {
toSelect = each;
}
if (each instanceof MasterDetailsComponent) {
((MasterDetailsComponent)each).setHistory(myHistory);
}
each.reset();
}
myHistory.clear();
if (toSelect == null && myName2Config.size() > 0) {
toSelect = myName2Config.iterator().next();
}
removeSelected();
navigateTo(toSelect != null ? createPlaceFor(toSelect) : null, false);
}
finally {
token.finish();
}
}
public ArtifactsStructureConfigurable getArtifactsStructureConfigurable() {
return myArtifactsStructureConfigurable;
}
@Override
public void disposeUIResources() {
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(myProject);
propertiesComponent.setValue("project.structure.last.edited", myUiState.lastEditedConfigurable);
myContext.getDaemonAnalyzer().stop();
for (Configurable each : myName2Config) {
each.disposeUIResources();
}
myProjectStructureDialog = null;
myContext.clear();
myName2Config.clear();
}
public History getHistory() {
return myHistory;
}
@Override
public void setHistory(final History history) {
myHistory = history;
}
@Override
public void queryPlace(@NotNull final Place place) {
place.putPath(CATEGORY, mySelectedConfigurable);
Place.queryFurther(mySelectedConfigurable, place);
}
public ActionCallback selectProjectGeneralSettings(final boolean requestFocus) {
Place place = createPlaceFor(myProjectConfig);
return navigateTo(place, requestFocus);
}
public ActionCallback select(@Nullable final String moduleToSelect, @Nullable String editorNameToSelect, final boolean requestFocus) {
Place place = createModulesPlace();
if (moduleToSelect != null) {
final Module module = ModuleManager.getInstance(myProject).findModuleByName(moduleToSelect);
assert module != null;
place = place.putPath(ModuleStructureConfigurable.TREE_OBJECT, module).putPath(ModuleEditor.SELECTED_EDITOR_NAME, editorNameToSelect);
}
return navigateTo(place, requestFocus);
}
public Place createModulesPlace() {
return createPlaceFor(myModulesConfig);
}
public Place createModulePlace(@NotNull Module module) {
return createModulesPlace().putPath(ModuleStructureConfigurable.TREE_OBJECT, module);
}
public ActionCallback select(@NotNull Sdk sdk, final boolean requestFocus) {
Place place = createPlaceFor(mySdkListConfigurable);
place.putPath(BaseStructureConfigurable.TREE_NAME, sdk.getName());
return navigateTo(place, requestFocus);
}
public ActionCallback selectProjectOrGlobalLibrary(@NotNull Library library, boolean requestFocus) {
Place place = createProjectOrGlobalLibraryPlace(library);
return navigateTo(place, requestFocus);
}
public Place createProjectOrGlobalLibraryPlace(Library library) {
Place place = createPlaceFor(getConfigurableFor(library));
place.putPath(BaseStructureConfigurable.TREE_NAME, library.getName());
return place;
}
public ActionCallback select(@Nullable Artifact artifact, boolean requestFocus) {
Place place = createArtifactPlace(artifact);
return navigateTo(place, requestFocus);
}
public Place createArtifactPlace(Artifact artifact) {
Place place = createPlaceFor(myArtifactsStructureConfigurable);
if (artifact != null) {
place.putPath(BaseStructureConfigurable.TREE_NAME, artifact.getName());
}
return place;
}
public ActionCallback select(@NotNull LibraryOrderEntry libraryOrderEntry, final boolean requestFocus) {
final Library lib = libraryOrderEntry.getLibrary();
if (lib == null || lib.getTable() == null) {
return selectOrderEntry(libraryOrderEntry.getOwnerModule(), libraryOrderEntry);
}
Place place = createPlaceFor(getConfigurableFor(lib));
place.putPath(BaseStructureConfigurable.TREE_NAME, libraryOrderEntry.getLibraryName());
return navigateTo(place, requestFocus);
}
public ActionCallback selectOrderEntry(@NotNull final Module module, @Nullable final OrderEntry orderEntry) {
return ModuleStructureConfigurable.getInstance(myProject).selectOrderEntry(module, orderEntry);
}
@Override
public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
if(myProjectStructureDialog == null) {
return ActionCallback.REJECTED;
}
final Configurable toSelect = (Configurable)place.getPath(CATEGORY);
if (mySelectedConfigurable != toSelect) {
if (mySelectedConfigurable instanceof BaseStructureConfigurable) {
((BaseStructureConfigurable)mySelectedConfigurable).onStructureUnselected();
}
removeSelected();
if (toSelect != null) {
myProjectStructureDialog.select(toSelect);
}
setSelectedConfigurable(toSelect);
if (toSelect instanceof MasterDetailsComponent) {
final MasterDetailsComponent masterDetails = (MasterDetailsComponent)toSelect;
masterDetails.setHistory(myHistory);
}
if (toSelect instanceof BaseStructureConfigurable) {
((BaseStructureConfigurable)toSelect).onStructureSelected();
}
}
final ActionCallback result = new ActionCallback();
Place.goFurther(toSelect, place, requestFocus).notifyWhenDone(result);
if (!myHistory.isNavigatingNow() && mySelectedConfigurable != null) {
myHistory.pushQueryPlace();
}
return result;
}
public void setSelectedConfigurable(Configurable toSelect) {
mySelectedConfigurable = toSelect;
if (mySelectedConfigurable != null) {
myUiState.lastEditedConfigurable = mySelectedConfigurable.getDisplayName();
}
}
private void removeSelected() {
mySelectedConfigurable = null;
myUiState.lastEditedConfigurable = null;
}
public static ProjectStructureConfigurable getInstance(final Project project) {
return ServiceManager.getService(project, ProjectStructureConfigurable.class);
}
public ProjectSdksModel getProjectSdksModel() {
return myProjectSdksModel;
}
public SdkListConfigurable getSdkConfigurable() {
if(mySdkListConfigurable == null) {
mySdkListConfigurable = SdkListConfigurable.getInstance(myProject);
mySdkListConfigurable.init(getContext());
}
return mySdkListConfigurable;
}
public ProjectLibrariesConfigurable getProjectLibrariesConfigurable() {
return myProjectLibrariesConfig;
}
public ModuleStructureConfigurable getModulesConfigurable() {
return myModulesConfig;
}
public ProjectConfigurable getProjectConfigurable() {
return myProjectConfig;
}
public void setConfigurablesForDispose(List<Configurable> name2Config) {
myName2Config.clear();
myName2Config.addAll(name2Config);
// we need up it
ContainerUtil.swapElements(myName2Config, ContainerUtil.indexOf(myName2Config, mySdkListConfigurable), 0);
ContainerUtil.swapElements(myName2Config, ContainerUtil.indexOf(myName2Config, myProjectLibrariesConfig), 1);
}
private static Place createPlaceFor(final Configurable configurable) {
return new Place().putPath(CATEGORY, configurable);
}
public StructureConfigurableContext getContext() {
return myContext;
}
public BaseLibrariesConfigurable getConfigurableFor(final Library library) {
if (LibraryTablesRegistrar.PROJECT_LEVEL.equals(library.getTable().getTableLevel())) {
return myProjectLibrariesConfig;
} else {
return null;
}
}
@Override
public JComponent getPreferredFocusedComponent() {
return null;
}
}