IJPL-207306 Fix UI in Subversion settings

- General

GitOrigin-RevId: 277750d2413bef47f5b46892b5afe5317e52bc57
This commit is contained in:
Pavel Porvatov
2025-09-15 08:49:36 +00:00
committed by intellij-monorepo-bot
parent 1e19c4e5d6
commit 64d4a1379c
6 changed files with 144 additions and 260 deletions
@@ -140,7 +140,7 @@
<search.projectOptionsTopHitProvider implementation="org.jetbrains.idea.svn.SvnOptionsTopHitProvider"/>
<vcs name="svn" vcsClass="org.jetbrains.idea.svn.SvnVcs" displayName="Subversion" administrativeAreaName=".svn, _svn"
areChildrenValidMappings="true"/>
<projectConfigurable id="vcs.Subversion" key="configurable.name.svn.general" instance="org.jetbrains.idea.svn.SvnConfigurable$General"
<projectConfigurable id="vcs.Subversion" key="configurable.name.svn.general" instance="org.jetbrains.idea.svn.GeneralSvnConfigurable"
parentId="project.propVCSSupport.Mappings" groupWeight="-500">
<configurable id="vcs.Subversion.Presentation" instance="org.jetbrains.idea.svn.PresentationSvnConfigurable"
bundle="messages.SvnBundle" key="configurable.name.svn.presentation"/>
@@ -279,8 +279,8 @@ dialog.title.select.path.to.subversion.executable=Subversion
label.select.path.to.subversion.executable=Select path to Subversion executable (1.7+)
label.path.to.svn.executable=Path to Subversion executable:
command.line.interactive.mode.title=Enable interactive mode
command.line.interactive.mode.description=<html>Emulates the behavior when Subversion commands are executed directly from the terminal (in the interactive mode).<br>\
This is required to handle password/passphrase prompts for svn+ssh repositories, and trust invalid server certificates for https repositories.</html>
command.line.interactive.mode.description=Emulates the behavior when Subversion commands are executed directly from the terminal (in the interactive mode).<br>\
This is required to handle password/passphrase prompts for svn+ssh repositories, and trust invalid server certificates for https repositories.
non.english.locale.detected.warning=A non-English locale is used.
ssh.settings.executable.label=SSH executable\:
@@ -1,128 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.idea.svn.GeneralSettingsPanel">
<grid id="2ae3a" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="7" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="0" y="0" width="760" height="343"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="137aa" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="6" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="decc5" class="javax.swing.JButton" binding="myClearAuthButton">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="1" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/SvnBundle" key="button.text.clear.authentication.cache"/>
</properties>
</component>
<component id="5b8dd" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<componentStyle value="SMALL"/>
<fontColor value="BRIGHTER"/>
<text resource-bundle="messages/SvnBundle" key="label.text.delete.stored.credentials"/>
</properties>
</component>
</children>
</grid>
<hspacer id="e8f24">
<constraints>
<grid row="6" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
<vspacer id="aa6b2">
<constraints>
<grid row="5" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="12735" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="44a8" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myCommandLineClient" custom-create="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="8f97d" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/SvnBundle" key="label.path.to.svn.executable"/>
</properties>
</component>
</children>
</grid>
<grid id="6abfd" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="3" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="40f21" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/SvnBundle" key="command.line.interactive.mode.description"/>
</properties>
</component>
</children>
</grid>
<grid id="f95cd" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="4" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="df054" class="javax.swing.JCheckBox" binding="myUseCustomConfigurationDirectory">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text resource-bundle="messages/SvnBundle" key="settings.use.custom.directory"/>
</properties>
</component>
<component id="212b3" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myConfigurationDirectoryText" custom-create="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<editable value="false"/>
</properties>
</component>
</children>
</grid>
<component id="df957" class="com.intellij.ui.components.JBCheckBox" binding="myRunUnderTerminal">
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<margin top="2" left="-2" bottom="2" right="3"/>
<text resource-bundle="messages/SvnBundle" key="command.line.interactive.mode.title"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -1,123 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.svn;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurableUi;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.ui.components.JBCheckBox;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier;
import javax.swing.*;
import java.util.Objects;
import static org.jetbrains.idea.svn.SvnBundle.message;
import static org.jetbrains.idea.svn.SvnUtil.USER_CONFIGURATION_PATH;
public class GeneralSettingsPanel implements ConfigurableUi<SvnConfiguration>, Disposable {
private final @NotNull Project myProject;
private JPanel myMainPanel;
private JCheckBox myUseCustomConfigurationDirectory;
private TextFieldWithBrowseButton myConfigurationDirectoryText;
private JButton myClearAuthButton;
private JBCheckBox myRunUnderTerminal;
private TextFieldWithBrowseButton myCommandLineClient;
public GeneralSettingsPanel(@NotNull Project project) {
myProject = project;
myUseCustomConfigurationDirectory.addActionListener(e -> {
boolean enabled = myUseCustomConfigurationDirectory.isSelected();
myConfigurationDirectoryText.setEnabled(enabled);
myConfigurationDirectoryText.setEditable(enabled);
SvnConfiguration configuration = SvnConfiguration.getInstance(myProject);
String path = configuration.getConfigurationDirectory();
if (!enabled || path == null) {
myConfigurationDirectoryText.setText(USER_CONFIGURATION_PATH.getValue().toString());
}
else {
myConfigurationDirectoryText.setText(path);
}
});
myCommandLineClient.addBrowseFolderListener(project, FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
.withTitle(message("dialog.title.select.path.to.subversion.executable"))
.withDescription(message("label.select.path.to.subversion.executable")));
myClearAuthButton.addActionListener(
e -> SvnAuthenticationNotifier.clearAuthenticationCache(myProject, myMainPanel, myConfigurationDirectoryText.getText()));
myConfigurationDirectoryText.addActionListener(e -> {
@NonNls String path = myConfigurationDirectoryText.getText().trim();
SvnConfigurable.selectConfigurationDirectory(path, s -> myConfigurationDirectoryText.setText(s), myProject, myMainPanel);
});
}
@Override
public @NotNull JComponent getComponent() {
return myMainPanel;
}
@Override
public void reset(@NotNull SvnConfiguration configuration) {
String path = configuration.getConfigurationDirectory();
if (configuration.isUseDefaultConfiguration() || path == null) {
path = USER_CONFIGURATION_PATH.getValue().toString();
}
myConfigurationDirectoryText.setText(path);
myUseCustomConfigurationDirectory.setSelected(!configuration.isUseDefaultConfiguration());
boolean enabled = myUseCustomConfigurationDirectory.isSelected();
myConfigurationDirectoryText.setEnabled(enabled);
myConfigurationDirectoryText.setEditable(enabled);
myRunUnderTerminal.setSelected(configuration.isRunUnderTerminal());
final SvnApplicationSettings applicationSettings17 = SvnApplicationSettings.getInstance();
myCommandLineClient.setText(applicationSettings17.getCommandLinePath());
}
@Override
public boolean isModified(@NotNull SvnConfiguration configuration) {
if (configuration.isUseDefaultConfiguration() == myUseCustomConfigurationDirectory.isSelected()) {
return true;
}
if (configuration.isRunUnderTerminal() != myRunUnderTerminal.isSelected()) return true;
final SvnApplicationSettings applicationSettings17 = SvnApplicationSettings.getInstance();
if (!Objects.equals(applicationSettings17.getCommandLinePath(), myCommandLineClient.getText().trim())) return true;
if (!configuration.getConfigurationDirectory().equals(myConfigurationDirectoryText.getText().trim())) return true;
return false;
}
@Override
public void apply(@NotNull SvnConfiguration configuration) {
configuration.setConfigurationDirParameters(!myUseCustomConfigurationDirectory.isSelected(), myConfigurationDirectoryText.getText());
final SvnVcs vcs17 = SvnVcs.getInstance(myProject);
final SvnApplicationSettings applicationSettings17 = SvnApplicationSettings.getInstance();
boolean reloadWorkingCopies = !StringUtil.equals(applicationSettings17.getCommandLinePath(), myCommandLineClient.getText().trim());
configuration.setRunUnderTerminal(myRunUnderTerminal.isSelected());
applicationSettings17.setCommandLinePath(myCommandLineClient.getText().trim());
boolean isClientValid = vcs17.checkCommandLineVersion();
if (!myProject.isDefault() && isClientValid && reloadWorkingCopies) {
vcs17.invokeRefreshSvnRoots();
VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty();
}
}
@Override
public void dispose() {
}
private void createUIComponents() {
myCommandLineClient = new TextFieldWithBrowseButton(null, this);
myConfigurationDirectoryText = new TextFieldWithBrowseButton(null, this);
}
}
@@ -0,0 +1,141 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.svn
import com.intellij.openapi.Disposable
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.options.BoundSearchableConfigurable
import com.intellij.openapi.options.Configurable.NoScroll
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogPanel
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.RightGap
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.layout.selected
import com.intellij.util.Consumer
import org.jetbrains.annotations.NonNls
import org.jetbrains.idea.svn.auth.SvnAuthenticationNotifier
internal class GeneralSvnConfigurable(private val project: Project) : BoundSearchableConfigurable(
SvnConfigurable.getGroupDisplayName(),
SvnConfigurable.HELP_ID,
SvnConfigurable.ID
), NoScroll, Disposable {
private val commandLineClient = TextFieldWithBrowseButton(null, this)
private val configurationDirectoryText = TextFieldWithBrowseButton(null, this)
private lateinit var useCustomConfigurationDirectory: JBCheckBox
init {
commandLineClient.addBrowseFolderListener(project, FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
.withTitle(SvnBundle.message("dialog.title.select.path.to.subversion.executable"))
.withDescription(SvnBundle.message("label.select.path.to.subversion.executable")))
}
override fun createPanel(): DialogPanel {
val settings = SvnConfiguration.getInstance(project)
lateinit var result: DialogPanel
result = panel {
row(SvnBundle.message("label.path.to.svn.executable")) {
val applicationSettings17 = SvnApplicationSettings.getInstance()
cell(commandLineClient)
.align(AlignX.FILL)
.onReset {
commandLineClient.setText(applicationSettings17.commandLinePath)
}.onApply {
applicationSettings17.commandLinePath = commandLineClient.getText().trim()
}.onIsModified {
applicationSettings17.commandLinePath != commandLineClient.getText().trim()
}
}
row {
checkBox(SvnBundle.message("command.line.interactive.mode.title"))
.comment(SvnBundle.message("command.line.interactive.mode.description"))
.bindSelected(settings::isRunUnderTerminal, settings::setRunUnderTerminal)
}
row {
useCustomConfigurationDirectory = checkBox(SvnBundle.message("settings.use.custom.directory"))
.onChanged {
val configuration = SvnConfiguration.getInstance(project)
val path = configuration.getConfigurationDirectory()
if (!useCustomConfigurationDirectory.isSelected || path == null) {
configurationDirectoryText.setText(SvnUtil.USER_CONFIGURATION_PATH.getValue().toString())
}
else {
configurationDirectoryText.setText(path)
}
}.gap(RightGap.SMALL)
.component
cell(configurationDirectoryText)
.align(AlignX.FILL)
.enabledIf(useCustomConfigurationDirectory.selected)
}
row {
label("")
}.resizableRow()
row {
button(SvnBundle.message("button.text.clear.authentication.cache")) {
SvnAuthenticationNotifier.clearAuthenticationCache(project, result, configurationDirectoryText.getText())
}.commentRight(SvnBundle.message("label.text.delete.stored.credentials"))
}
}
configurationDirectoryText.addActionListener { _ ->
@NonNls val path = configurationDirectoryText.getText().trim()
SvnConfigurable.selectConfigurationDirectory(path, Consumer { s: String? -> configurationDirectoryText.setText(s) }, project, result)
}
return result
}
override fun apply() {
val settings = SvnConfiguration.getInstance(project)
val applicationSettings17 = SvnApplicationSettings.getInstance()
val oldCommandLinePath = applicationSettings17.commandLinePath
super.apply()
settings.setConfigurationDirParameters(!useCustomConfigurationDirectory.isSelected, configurationDirectoryText.getText())
val vcs17 = SvnVcs.getInstance(project)
val isClientValid = vcs17.checkCommandLineVersion()
if (!project.isDefault && isClientValid && oldCommandLinePath != applicationSettings17.commandLinePath) {
vcs17.invokeRefreshSvnRoots()
VcsDirtyScopeManager.getInstance(project).markEverythingDirty()
}
}
override fun isModified(): Boolean {
val settings = SvnConfiguration.getInstance(project)
if (settings.isUseDefaultConfiguration == useCustomConfigurationDirectory.isSelected
|| settings.getConfigurationDirectory() != configurationDirectoryText.getText().trim()) {
return true
}
return super.isModified()
}
override fun reset() {
super.reset()
val settings = SvnConfiguration.getInstance(project)
var path = settings.getConfigurationDirectory()
if (settings.isUseDefaultConfiguration || path == null) {
path = SvnUtil.USER_CONFIGURATION_PATH.getValue().toString()
}
configurationDirectoryText.setText(path)
useCustomConfigurationDirectory.setSelected(!settings.isUseDefaultConfiguration)
}
override fun dispose() {
}
}
@@ -62,12 +62,6 @@ public abstract class SvnConfigurable extends ConfigurableBase<ConfigurableUi<Sv
return SvnConfiguration.getInstance(myProject);
}
public static class General extends SvnConfigurable {
public General(@NotNull Project project) {
super(project, ID, getGroupDisplayName(), () -> new GeneralSettingsPanel(project), HELP_ID);
}
}
public static class Ssh extends SvnConfigurable {
public Ssh(@NotNull Project project) {
super(project, "SSH", message("configurable.name.svn.ssh"), () -> new SshSettingsPanel(project));