diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/FileAppearanceServiceImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/FileAppearanceServiceImpl.java
new file mode 100644
index 000000000000..277e35f16030
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/FileAppearanceServiceImpl.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.roots.ui.util.*;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileSystem;
+import com.intellij.openapi.vfs.ex.http.HttpFileSystem;
+import com.intellij.ui.HtmlListCellRenderer;
+import com.intellij.ui.SimpleColoredComponent;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+public class FileAppearanceServiceImpl extends FileAppearanceService {
+ private static CellAppearanceEx EMPTY = new CellAppearanceEx() {
+ @Override
+ public void customize(SimpleColoredComponent component) { }
+
+ @Override
+ public void customize(@NotNull HtmlListCellRenderer renderer) { }
+
+ @Override
+ public String getText() { return ""; }
+ };
+
+ @NotNull
+ @Override
+ public CellAppearanceEx empty() {
+ return EMPTY;
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forVirtualFile(@NotNull final VirtualFile file) {
+ if (!file.isValid()) {
+ return forInvalidUrl(file.getPresentableUrl());
+ }
+
+ final VirtualFileSystem fileSystem = file.getFileSystem();
+ if (fileSystem.getProtocol().equals(JarFileSystem.PROTOCOL)) {
+ return new JarSubfileCellAppearance(file);
+ }
+ if (fileSystem instanceof HttpFileSystem) {
+ return new HttpUrlCellAppearance(file);
+ }
+ if (file.isDirectory()) {
+ return SimpleTextCellAppearance.regular(file.getPresentableUrl(), PlatformIcons.FOLDER_ICON);
+ }
+ return new ValidFileCellAppearance(file);
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forIoFile(@NotNull final File file) {
+ final String absolutePath = file.getAbsolutePath();
+ if (!file.exists()) {
+ return forInvalidUrl(absolutePath);
+ }
+
+ if (file.isDirectory()) {
+ return SimpleTextCellAppearance.regular(absolutePath, PlatformIcons.FOLDER_ICON);
+ }
+
+ final String name = file.getName();
+ final FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(name);
+ final File parent = file.getParentFile();
+ final CompositeAppearance appearance = CompositeAppearance.textComment(name, parent.getAbsolutePath());
+ appearance.setIcon(fileType.getIcon());
+ return appearance;
+ }
+
+ @NotNull
+ public CellAppearanceEx forInvalidUrl(@NotNull final String text) {
+ return SimpleTextCellAppearance.invalid(text, PlatformIcons.INVALID_ENTRY_ICON);
+ }
+}
diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceServiceImpl.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceServiceImpl.java
new file mode 100644
index 000000000000..e54f7796b3dd
--- /dev/null
+++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceServiceImpl.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectBundle;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
+import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
+import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager;
+import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
+import com.intellij.openapi.roots.ui.util.CompositeAppearance;
+import com.intellij.openapi.roots.ui.util.SimpleTextCellAppearance;
+import com.intellij.openapi.util.IconLoader;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.JarFileSystem;
+import com.intellij.openapi.vfs.VfsUtilCore;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.vfs.VirtualFileManager;
+import com.intellij.ui.SimpleTextAttributes;
+import com.intellij.util.PathUtil;
+import com.intellij.util.PlatformIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.File;
+
+public class OrderEntryAppearanceServiceImpl extends OrderEntryAppearanceService {
+ private static final Icon EXCLUDE_FOLDER_ICON = IconLoader.getDisabledIcon(PlatformIcons.FOLDER_ICON);
+
+ private static final String NO_JDK = ProjectBundle.message("jdk.missing.item");
+
+ private final Project myProject;
+
+ public OrderEntryAppearanceServiceImpl(@NotNull final Project project) {
+ myProject = project;
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forOrderEntry(@NotNull final OrderEntry orderEntry, final boolean selected) {
+ if (orderEntry instanceof JdkOrderEntry) {
+ JdkOrderEntry jdkLibraryEntry = (JdkOrderEntry)orderEntry;
+ Sdk jdk = jdkLibraryEntry.getJdk();
+ if (!orderEntry.isValid()) {
+ final String oldJdkName = jdkLibraryEntry.getJdkName();
+ return FileAppearanceService.getInstance().forInvalidUrl(oldJdkName != null ? oldJdkName : NO_JDK);
+ }
+ return forJdk(jdk, false, selected, true);
+ }
+ else if (!orderEntry.isValid()) {
+ return FileAppearanceService.getInstance().forInvalidUrl(orderEntry.getPresentableName());
+ }
+ else if (orderEntry instanceof LibraryOrderEntry) {
+ LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
+ if (!libraryOrderEntry.isValid()) { //library can be removed
+ return FileAppearanceService.getInstance().forInvalidUrl(orderEntry.getPresentableName());
+ }
+ Library library = libraryOrderEntry.getLibrary();
+ assert library != null : libraryOrderEntry;
+ return forLibrary(library, false);
+ }
+ else if (orderEntry.isSynthetic()) {
+ String presentableName = orderEntry.getPresentableName();
+ Icon icon = orderEntry instanceof ModuleSourceOrderEntry ? sourceFolderIcon(false) : null;
+ return new SimpleTextCellAppearance(presentableName, icon, SimpleTextAttributes.SYNTHETIC_ATTRIBUTES);
+ }
+ else if (orderEntry instanceof ModuleOrderEntry) {
+ final Icon icon = ModuleType.get(((ModuleOrderEntry)orderEntry).getModule()).getNodeIcon(false);
+ return SimpleTextCellAppearance.regular(orderEntry.getPresentableName(), icon);
+ }
+ else {
+ return CompositeAppearance.single(orderEntry.getPresentableName());
+ }
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forLibrary(@NotNull final Library library, final boolean hasInvalidRoots) {
+ final StructureConfigurableContext context = ProjectStructureConfigurable.getInstance(myProject).getContext();
+ final Icon icon = LibraryPresentationManager.getInstance().getCustomIcon(library, context);
+
+ final String name = library.getName();
+ if (name != null) {
+ return normalOrRedWaved(name, (icon != null ? icon : PlatformIcons.LIBRARY_ICON), hasInvalidRoots);
+ }
+
+ final String[] files = library.getUrls(OrderRootType.CLASSES);
+ if (files.length == 0) {
+ return SimpleTextCellAppearance.invalid(ProjectBundle.message("library.empty.library.item"), PlatformIcons.LIBRARY_ICON);
+ }
+ else if (files.length == 1) {
+ return forVirtualFilePointer(new LightFilePointer(files[0]));
+ }
+
+ final String url = StringUtil.trimEnd(files[0], JarFileSystem.JAR_SEPARATOR);
+ return SimpleTextCellAppearance.regular(PathUtil.getFileName(url), PlatformIcons.LIBRARY_ICON);
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forJdk(@Nullable final Sdk jdk, final boolean isInComboBox, final boolean selected, final boolean showVersion) {
+ if (jdk == null) {
+ return FileAppearanceService.getInstance().forInvalidUrl(NO_JDK);
+ }
+
+ String name = jdk.getName();
+ CompositeAppearance appearance = new CompositeAppearance();
+ appearance.setIcon(jdk.getSdkType().getIcon());
+ VirtualFile homeDirectory = jdk.getHomeDirectory();
+ SimpleTextAttributes attributes = getTextAttributes(homeDirectory != null && homeDirectory.isValid(), selected);
+ CompositeAppearance.DequeEnd ending = appearance.getEnding();
+ ending.addText(name, attributes);
+
+ if (showVersion) {
+ String versionString = jdk.getVersionString();
+ if (versionString != null && !versionString.equals(name)) {
+ SimpleTextAttributes textAttributes = isInComboBox ? SimpleTextAttributes.SYNTHETIC_ATTRIBUTES : SimpleTextAttributes.GRAY_ATTRIBUTES;
+ ending.addComment(versionString, textAttributes);
+ }
+ }
+
+ return ending.getAppearance();
+ }
+
+ private static SimpleTextAttributes getTextAttributes(final boolean valid, final boolean selected) {
+ if (!valid) {
+ return SimpleTextAttributes.ERROR_ATTRIBUTES;
+ }
+ else if (selected) {
+ return SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES;
+ }
+ else {
+ return SimpleTextAttributes.SIMPLE_CELL_ATTRIBUTES;
+ }
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forContentFolder(@NotNull final ContentFolder folder) {
+ if (folder instanceof SourceFolder) {
+ return formatRelativePath(folder, PlatformIcons.FOLDER_ICON);
+ }
+ else if (folder instanceof ExcludeFolder) {
+ return formatRelativePath(folder, EXCLUDE_FOLDER_ICON);
+ }
+ else {
+ throw new RuntimeException(folder.getClass().getName());
+ }
+ }
+
+ @NotNull
+ @Override
+ public CellAppearanceEx forModule(@NotNull final Module module) {
+ return SimpleTextCellAppearance.regular(module.getName(), ModuleType.get(module).getNodeIcon(false));
+ }
+
+ @NotNull
+ private static Icon sourceFolderIcon(final boolean testSource) {
+ return testSource ? PlatformIcons.TEST_SOURCE_FOLDER : PlatformIcons.SOURCE_FOLDERS_ICON;
+ }
+
+ @NotNull
+ private static CellAppearanceEx normalOrRedWaved(@NotNull final String text, @Nullable final Icon icon, final boolean waved) {
+ return waved ? new SimpleTextCellAppearance(text, icon, new SimpleTextAttributes(SimpleTextAttributes.STYLE_WAVED, null, Color.RED))
+ : SimpleTextCellAppearance.regular(text, icon);
+ }
+
+ @NotNull
+ private static CellAppearanceEx forVirtualFilePointer(@NotNull final LightFilePointer filePointer) {
+ final VirtualFile file = filePointer.getFile();
+ return file != null ? FileAppearanceService.getInstance().forVirtualFile(file)
+ : FileAppearanceService.getInstance().forInvalidUrl(filePointer.getPresentableUrl());
+ }
+
+ @NotNull
+ private static CellAppearanceEx formatRelativePath(@NotNull final ContentFolder folder, @NotNull final Icon icon) {
+ LightFilePointer folderFile = new LightFilePointer(folder.getUrl());
+ VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(folder.getContentEntry().getUrl());
+ if (file == null) return FileAppearanceService.getInstance().forInvalidUrl(folderFile.getPresentableUrl());
+
+ String contentPath = file.getPath();
+ String relativePath;
+ SimpleTextAttributes textAttributes;
+ VirtualFile folderFileFile = folderFile.getFile();
+ if (folderFileFile == null) {
+ String absolutePath = folderFile.getPresentableUrl();
+ relativePath = absolutePath.startsWith(contentPath) ? absolutePath.substring(contentPath.length()) : absolutePath;
+ textAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
+ }
+ else {
+ relativePath = VfsUtilCore.getRelativePath(folderFileFile, file, File.separatorChar);
+ textAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
+ }
+
+ relativePath = StringUtil.isEmpty(relativePath) ? "." + File.separatorChar : relativePath;
+ return new SimpleTextCellAppearance(relativePath, icon, textAttributes);
+ }
+}
diff --git a/platform/lang-api/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceService.java b/platform/lang-api/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceService.java
new file mode 100644
index 000000000000..b7359020ca04
--- /dev/null
+++ b/platform/lang-api/src/com/intellij/openapi/roots/ui/OrderEntryAppearanceService.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.ContentFolder;
+import com.intellij.openapi.roots.OrderEntry;
+import com.intellij.openapi.roots.libraries.Library;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class OrderEntryAppearanceService {
+ public static OrderEntryAppearanceService getInstance(@NotNull final Project project) {
+ return ServiceManager.getService(project, OrderEntryAppearanceService.class);
+ }
+
+ @NotNull
+ public abstract CellAppearanceEx forOrderEntry(@NotNull OrderEntry orderEntry, boolean selected);
+
+ @NotNull
+ public abstract CellAppearanceEx forLibrary(@NotNull Library library, boolean hasInvalidRoots);
+
+ @NotNull
+ public abstract CellAppearanceEx forJdk(@Nullable Sdk jdk, boolean isInComboBox, boolean selected, boolean showVersion);
+
+ @NotNull
+ public abstract CellAppearanceEx forContentFolder(@NotNull ContentFolder folder);
+
+ @NotNull
+ public abstract CellAppearanceEx forModule(@NotNull Module module);
+}
diff --git a/platform/platform-api/src/com/intellij/openapi/roots/ui/CellAppearanceEx.java b/platform/platform-api/src/com/intellij/openapi/roots/ui/CellAppearanceEx.java
new file mode 100644
index 000000000000..bfc7d948c870
--- /dev/null
+++ b/platform/platform-api/src/com/intellij/openapi/roots/ui/CellAppearanceEx.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.roots.ui.util.CellAppearance;
+import com.intellij.ui.HtmlListCellRenderer;
+import org.jetbrains.annotations.NotNull;
+
+public interface CellAppearanceEx extends CellAppearance {
+ void customize(@NotNull HtmlListCellRenderer renderer);
+}
diff --git a/platform/platform-api/src/com/intellij/openapi/roots/ui/FileAppearanceService.java b/platform/platform-api/src/com/intellij/openapi/roots/ui/FileAppearanceService.java
new file mode 100644
index 000000000000..e1ef1a89d917
--- /dev/null
+++ b/platform/platform-api/src/com/intellij/openapi/roots/ui/FileAppearanceService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2000-2011 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;
+
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+public abstract class FileAppearanceService {
+ public static FileAppearanceService getInstance() {
+ return ServiceManager.getService(FileAppearanceService.class);
+ }
+
+ @NotNull
+ public abstract CellAppearanceEx empty();
+
+ @NotNull
+ public abstract CellAppearanceEx forVirtualFile(@NotNull VirtualFile file);
+
+ @NotNull
+ public abstract CellAppearanceEx forIoFile(@NotNull File file);
+
+ @NotNull
+ public abstract CellAppearanceEx forInvalidUrl(@NotNull String url);
+}
diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml
index 69ca6878ffac..aaf13708ec78 100644
--- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml
+++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml
@@ -130,6 +130,9 @@
+
+
+ serviceImplementation="com.intellij.execution.testframework.export.ExportTestResultsConfiguration"/>
+ serviceImplementation="com.intellij.execution.testframework.autotest.AutoTestManager"/>
+
+
+