[platform] amending file filters in platform file choosers with extension filters, instead of replacing (IJPL-150176)

GitOrigin-RevId: bc5d9ab58303a65b186cb36dc8099975f7b6b8ba
This commit is contained in:
Roman Shevchenko
2024-09-27 21:56:54 +02:00
committed by intellij-monorepo-bot
parent 6547c67d6d
commit 16ad6dea8a
13 changed files with 70 additions and 40 deletions

View File

@@ -220,6 +220,7 @@ public class BackgroundImageDialog extends DialogWrapper {
((CardLayout)myPreviewPanel.getLayout()).show(myPreviewPanel, EDITOR);
myPathField.getComboBox().setEditable(true);
var descriptor = new FileChooserDescriptor(true, false, false, false, true, false)
.withFileFilter(file -> ImageFileTypeManager.getInstance().isImage(file))
.withExtensionFilter(ImageFileTypeManager.getInstance().getImageFileType());
myPathField.addBrowseFolderListener(null, descriptor, TextComponentAccessor.STRING_COMBOBOX_WHOLE_TEXT);
JTextComponent textComponent = getComboEditor();

View File

@@ -212,6 +212,7 @@ class PasswordSafeConfigurableUi(private val settings: PasswordSafeSettings) : C
row(CredentialStoreBundle.message("settings.password.database")) {
val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor()
.withTitle(CredentialStoreBundle.message("passwordSafeConfigurable.keepass.database.file"))
.withFileFilter { it.extension.equals("kdbx", ignoreCase = true) }
.withExtensionFilter("kdbx")
keePassDbFile = textFieldWithBrowseButton(fileChooserDescriptor, fileChosen = {
val path = if (it.isDirectory) "${it.path}${File.separator}${DB_FILE_NAME}" else it.path

View File

@@ -21,6 +21,7 @@ import org.jetbrains.annotations.*;
import javax.swing.*;
import java.util.*;
import java.util.function.Predicate;
/**
* Allows customizing {@link FileChooser} dialog options.
@@ -46,7 +47,7 @@ public class FileChooserDescriptor implements Cloneable {
private boolean myTreeRootVisible = false;
private boolean myShowHiddenFiles = false;
private @Nullable Pair<@Nls String, List<String>> myExtensionFilter = null;
private Condition<? super VirtualFile> myFileFilter = null;
private Predicate<? super VirtualFile> myFileFilter = null;
private boolean myForcedToUseIdeaFileChooser = false;
private final Map<String, Object> myUserData = new HashMap<>();
@@ -87,7 +88,7 @@ public class FileChooserDescriptor implements Cloneable {
myTreeRootVisible = d.isTreeRootVisible();
myShowHiddenFiles = d.isShowHiddenFiles();
myExtensionFilter = d.myExtensionFilter;
myFileFilter = null;
myFileFilter = d.myFileFilter;
myForcedToUseIdeaFileChooser = false;
myUserData.putAll(d.myUserData);
}
@@ -209,11 +210,13 @@ public class FileChooserDescriptor implements Cloneable {
return this;
}
/** @deprecated incompatible with native file choosers; please use {@link #withExtensionFilter} instead */
@Deprecated
/**
* Sets simple boolean condition for use in {@link #isFileVisible(VirtualFile, boolean)} and {@link #isFileSelectable(VirtualFile)}.
* <p/>
* In native choosers, has no effect on visibility (use {@link #withExtensionFilter} for that), only on a final eligibility check.
*/
public FileChooserDescriptor withFileFilter(@Nullable Condition<? super VirtualFile> filter) {
myFileFilter = filter;
putUserData(FILTER_TYPE, filter != null ? "other" : null);
return this;
}
@@ -245,14 +248,13 @@ public class FileChooserDescriptor implements Cloneable {
if (extensions.length == 0) throw new IllegalArgumentException("The list must not be empty");
if (ContainerUtil.find(extensions, String::isBlank) != null) throw new IllegalArgumentException("The list must not contain empty strings");
myExtensionFilter = new Pair<>(label, List.of(extensions));
putUserData(FILTER_TYPE, "file-ext");
return this;
}
/**
* Defines whether a file is visible in the tree.
*
* @deprecated ignored by native file choosers; do not use.
* @deprecated ignored by native file choosers; use {@link #withFileFilter} and {@link #withExtensionFilter} instead.
*/
@Deprecated
public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
@@ -272,7 +274,7 @@ public class FileChooserDescriptor implements Cloneable {
if (myExtensionFilter != null && !matchesExtFilter(file, myExtensionFilter.second)) {
return false;
}
if (myFileFilter != null && !myFileFilter.value(file)) {
if (myFileFilter != null && !myFileFilter.test(file)) {
return false;
}
}
@@ -306,7 +308,7 @@ public class FileChooserDescriptor implements Cloneable {
return matchesExtFilter(file, myExtensionFilter.second);
}
if (myFileFilter != null) {
return myFileFilter.value(file);
return myFileFilter.test(file);
}
}
return acceptAsJarFile(file) || acceptAsGeneralFile(file);
@@ -395,8 +397,17 @@ public class FileChooserDescriptor implements Cloneable {
}
public @Nullable <T> T getUserData(@NotNull DataKey<T> key) {
@SuppressWarnings("unchecked") T t = (T)myUserData.get(key.getName());
return t;
if (key == FILTER_TYPE) {
var result = "";
if (myExtensionFilter != null) result += 'e';
if (myFileFilter != null) result += 'f';
@SuppressWarnings("unchecked") T t = (T)result;
return t;
}
else {
@SuppressWarnings("unchecked") T t = (T)myUserData.get(key.getName());
return t;
}
}
private static boolean matchesExtFilter(VirtualFile file, List<String> extensions) {

View File

@@ -2,7 +2,9 @@
package com.intellij.openapi.fileChooser;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.Strings;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,15 +49,21 @@ public final class FileChooserDescriptorFactory {
}
public static FileChooserDescriptor createSingleFileDescriptor(@NotNull FileType fileType) {
return createSingleFileNoJarsDescriptor().withExtensionFilter(fileType);
return createSingleFileNoJarsDescriptor()
.withFileFilter(file -> FileTypeRegistry.getInstance().isFileOfType(file, fileType))
.withExtensionFilter(fileType);
}
public static FileChooserDescriptor createSingleFileOrFolderDescriptor(@NotNull FileType fileType) {
return createSingleFileOrFolderDescriptor().withExtensionFilter(fileType);
return createSingleFileOrFolderDescriptor()
.withFileFilter(file -> file.isDirectory() || FileTypeRegistry.getInstance().isFileOfType(file, fileType))
.withExtensionFilter(fileType);
}
public static FileChooserDescriptor createSingleFileDescriptor(@NotNull String extension) {
return createSingleFileNoJarsDescriptor().withExtensionFilter(extension);
return createSingleFileNoJarsDescriptor()
.withFileFilter(file -> Strings.endsWithIgnoreCase(file.getName(), '.' + extension))
.withExtensionFilter(extension);
}
public static FileChooserDescriptor createSingleFolderDescriptor() {

View File

@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -63,8 +64,8 @@ public final class ViewOfflineResultsAction extends AnAction {
LOG.assertTrue(project != null);
var descriptor = FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor()
.withExtensionFilter("xml")
var xmlFileType = FileTypeManager.getInstance().getStdFileType("XML");
var descriptor = FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor(xmlFileType)
.withTitle(InspectionsBundle.message("view.offline.inspections.select.path.title"))
.withDescription(InspectionsBundle.message("view.offline.inspections.select.path.description"));
final VirtualFile virtualFile = FileChooser.chooseFile(descriptor, project, null);

View File

@@ -54,20 +54,16 @@ public final class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunn
private static final Color CODE_COLOR = PlatformColors.BLUE;
private static final Color TEST_COLOR = JBColor.GREEN.darker();
private static final FileChooserDescriptor FILE_DESCRIPTOR = new ScriptFileChooserDescriptor();
private JPanel myComponent;
private PlaybackRunner runner;
private JEditorPane myLog;
private final JTextField myCurrentScript = new JTextField();
private VirtualFileListener myVfsListener;
private boolean myChanged;
private PlaybackDebuggerState myState;
private static final FileChooserDescriptor FILE_DESCRIPTOR = new ScriptFileChooserDescriptor();
private JTextArea myCodeEditor;
private void initUi() {
@@ -155,6 +151,7 @@ public final class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunn
super(true, false, false, false, false, false);
putUserData(FileChooserKeys.NEW_FILE_TYPE, UiScriptFileType.getInstance());
putUserData(FileChooserKeys.NEW_FILE_TEMPLATE_TEXT, "");
withFileFilter(file -> UiScriptFileType.myExtension.equalsIgnoreCase(file.getExtension()));
withExtensionFilter(UiScriptFileType.myExtension);
}
}

View File

@@ -196,6 +196,7 @@ internal class BrowseIconsComboBox(private val customActionsSchema: CustomAction
private fun browseIconAndSelect() {
val descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
.withFileFilter { "svg".equals(it.extension, ignoreCase = true) || "png".equals(it.extension, ignoreCase = true) }
.withExtensionFilter(IdeBundle.message("icon.file.filter.label"), "svg", "png")
descriptor.title = IdeBundle.message("title.browse.icon")
descriptor.description = IdeBundle.message("prompt.browse.icon.for.selected.action")

View File

@@ -6,6 +6,7 @@ import com.intellij.diagnostic.VMOptions;
import com.intellij.ide.BootstrapBundle;
import com.intellij.ide.GeneralSettings;
import com.intellij.ide.actions.ImportSettingsFilenameFilter;
import com.intellij.ide.highlighter.ArchiveFileType;
import com.intellij.ide.plugins.*;
import com.intellij.ide.plugins.marketplace.MarketplacePluginDownloadService;
import com.intellij.ide.startup.StartupActionScriptManager;
@@ -19,6 +20,7 @@ import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.keymap.impl.KeymapManagerImpl;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProcessCanceledException;
@@ -479,7 +481,9 @@ public final class ConfigImportHelper {
}
public static void setSettingsFilter(@NotNull FileChooserDescriptor descriptor) {
descriptor.withExtensionFilter(BootstrapBundle.message("import.settings.filter"), "zip", "jar");
descriptor
.withFileFilter(file -> FileTypeRegistry.getInstance().isFileOfType(file, ArchiveFileType.INSTANCE))
.withExtensionFilter(BootstrapBundle.message("import.settings.filter"), "zip", "jar");
}
public static void setConfigImportedInThisSession() {

View File

@@ -13,39 +13,39 @@ import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import static java.util.Objects.requireNonNullElse;
@ApiStatus.Internal
public class FileChooserUsageCollector extends CounterUsagesCollector {
private static final EventLogGroup GROUP = new EventLogGroup("ui.file.chooser", 3);
private static final EventLogGroup GROUP = new EventLogGroup("ui.file.chooser", 4);
private static final EnumEventField<Type> TYPE = EventFields.Enum("type", Type.class);
private static final BooleanEventField FORCED = EventFields.Boolean("forced");
private static final BooleanEventField JAR_CONTENTS = EventFields.Boolean("jar_contents");
private static final BooleanEventField NON_LOCAL_ROOTS = EventFields.Boolean("non_local_roots");
private static final EnumEventField<Filter> FILTER = EventFields.Enum("filter", Filter.class);
private static final BooleanEventField EXT_FILTER = EventFields.Boolean("ext_filter");
private static final BooleanEventField FILE_FILTER = EventFields.Boolean("file_filter");
private static final BooleanEventField NON_LOCAL_FILES = EventFields.Boolean("non_local_files");
private static final VarargEventId CHOOSER_SHOWN = GROUP.registerVarargEvent(
"chooser_shown", TYPE, FORCED, JAR_CONTENTS, NON_LOCAL_ROOTS, FILTER, NON_LOCAL_FILES);
"chooser_shown", TYPE, FORCED, JAR_CONTENTS, NON_LOCAL_ROOTS, EXT_FILTER, FILE_FILTER, NON_LOCAL_FILES);
private enum Type {NATIVE, CLASSIC, NEW, OTHER}
private static Filter filterType(@Nullable String filterType) {
return filterType == null ? Filter.NONE : filterType.equals("file-ext") ? Filter.EXT : Filter.OTHER;
}
@Override
public EventLogGroup getGroup() {
return GROUP;
}
public static void log(FileChooserDialog chooser, FileChooserDescriptor descriptor, VirtualFile[] files) {
var filter = requireNonNullElse(descriptor.getUserData(FileChooserDescriptor.FILTER_TYPE), "");
CHOOSER_SHOWN.log(
TYPE.with(chooserType(chooser)),
FORCED.with(descriptor.isForcedToUseIdeaFileChooser()),
JAR_CONTENTS.with(descriptor.isChooseJarContents()),
NON_LOCAL_ROOTS.with(ContainerUtil.exists(descriptor.getRoots(), root -> !root.isInLocalFileSystem())),
FILTER.with(filterType(descriptor.getUserData(FileChooserDescriptor.FILTER_TYPE))),
EXT_FILTER.with(filter.indexOf('e') != 0),
FILE_FILTER.with(filter.indexOf('f') != 0),
NON_LOCAL_FILES.with(ContainerUtil.exists(files, file -> !file.isInLocalFileSystem()))
);
}
@@ -56,6 +56,4 @@ public class FileChooserUsageCollector extends CounterUsagesCollector {
chooser instanceof FileChooserDialogImpl ? Type.CLASSIC :
Type.OTHER;
}
private enum Filter {NONE, EXT, OTHER}
}

View File

@@ -28,7 +28,7 @@ public final class SchemeImportUtil {
if (sourceExtensions.length == 1) {
descriptor.withExtensionFilter(sourceExtensions[0]);
}
else {
else if (sourceExtensions.length > 1) {
descriptor.withExtensionFilter(IdeCoreBundle.message("file.chooser.files.label", sourceExtensions[0].toUpperCase(Locale.ROOT)), sourceExtensions);
}
if (description != null) {

View File

@@ -10,6 +10,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileChooser.FileChooser
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.ide.CopyPasteManager
import com.intellij.openapi.progress.runBackgroundableTask
import com.intellij.openapi.vfs.VfsUtilCore
@@ -75,8 +76,10 @@ internal open class DumpInvalidTipsAction : AnAction() {
internal class SelectAndDumpInvalidTipsAction : DumpInvalidTipsAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = e.getData(CommonDataKeys.PROJECT)
val htmlFileType = FileTypeManager.getInstance().getStdFileType("HTML")
val descriptor = FileChooserDescriptor(true, true, false, false, false, true)
.withExtensionFilter(FileTypeManager.getInstance().getStdFileType("HTML"))
.withFileFilter { FileTypeRegistry.getInstance().isFileOfType(it, htmlFileType) }
.withExtensionFilter(htmlFileType)
.withDescription("Choose HTML files or folders with tips.")
val chosenFiles = FileChooser.chooseFiles(descriptor, project, null)

View File

@@ -4,10 +4,10 @@ package com.intellij.ide.util;
import com.intellij.CommonBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.TipsOfTheDayUsagesCollector;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.registry.Registry;
@@ -137,8 +137,10 @@ final class TipDialog extends DialogWrapper {
@Override
public void actionPerformed(ActionEvent e) {
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createMultipleFilesNoJarsDescriptor()
.withExtensionFilter(FileTypeManager.getInstance().getStdFileType("HTML"));
var htmlFileType = FileTypeManager.getInstance().getStdFileType("HTML");
var descriptor = FileChooserDescriptorFactory.createMultipleFilesNoJarsDescriptor()
.withFileFilter(file -> FileTypeRegistry.getInstance().isFileOfType(file, htmlFileType))
.withExtensionFilter(htmlFileType);
String value = propertiesComponent.getValue(LAST_OPENED_TIP_PATH);
VirtualFile lastOpenedTip = value != null ? LocalFileSystem.getInstance().findFileByPath(value) : null;
VirtualFile[] pathToSelect = lastOpenedTip != null ? new VirtualFile[]{lastOpenedTip} : VirtualFile.EMPTY_ARRAY;

View File

@@ -13,6 +13,7 @@ import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.fileChooser.FileSaverDescriptor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.SearchableConfigurable;
@@ -704,8 +705,10 @@ public final class InjectionsSettingsUI extends SearchableConfigurable.Parent.Ab
}
private void doImportAction(final DataContext dataContext) {
var xmlFileType = FileTypeManager.getInstance().getStdFileType("XML");
var descriptor = new FileChooserDescriptor(true, false, false, false, true, false)
.withExtensionFilter(FileTypeManager.getInstance().getStdFileType("XML"))
.withFileFilter(file -> FileTypeRegistry.getInstance().isFileOfType(file, xmlFileType))
.withExtensionFilter(xmlFileType)
.withTitle(IntelliLangBundle.message("dialog.file.chooser.title.import.configuration"))
.withDescription(IntelliLangBundle.message("dialog.file.chooser.description.please.select.the.configuration.file"));