12f1d7256e66525ab09043eaa969bf2748a5a7b6
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / configurable / VcsGeneralSettingsConfigurable.kt
1 // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.openapi.vcs.configurable
3
4 import com.intellij.application.options.editor.checkBox
5 import com.intellij.ide.actions.RevealFileAction
6 import com.intellij.openapi.application.ApplicationNamesInfo
7 import com.intellij.openapi.application.runInEdt
8 import com.intellij.openapi.extensions.ExtensionPointName
9 import com.intellij.openapi.options.BoundCompositeConfigurable
10 import com.intellij.openapi.options.Configurable
11 import com.intellij.openapi.options.Configurable.NoScroll
12 import com.intellij.openapi.options.ConfigurableEP
13 import com.intellij.openapi.options.UnnamedConfigurable
14 import com.intellij.openapi.project.Project
15 import com.intellij.openapi.ui.ComboBox
16 import com.intellij.openapi.ui.DialogPanel
17 import com.intellij.openapi.vcs.ProjectLevelVcsManager
18 import com.intellij.openapi.vcs.VcsBundle.message
19 import com.intellij.openapi.vcs.VcsConfiguration
20 import com.intellij.openapi.vcs.VcsListener
21 import com.intellij.openapi.vcs.VcsShowConfirmationOption
22 import com.intellij.openapi.vcs.changes.RemoteRevisionsCache
23 import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings
24 import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx
25 import com.intellij.openapi.vcs.impl.VcsEP
26 import com.intellij.openapi.vcs.impl.projectlevelman.PersistentVcsSetting
27 import com.intellij.ui.EnumComboBoxModel
28 import com.intellij.ui.layout.*
29 import com.intellij.vcsUtil.VcsUtil
30 import org.jetbrains.annotations.Nls
31 import javax.swing.JComponent
32
33 internal class GeneralVcSettingsProviderEP(project: Project) : ConfigurableEP<UnnamedConfigurable>(project)
34
35 private val VCS_SETTINGS_EP_NAME = ExtensionPointName<GeneralVcSettingsProviderEP>("com.intellij.generalVcsSettingsExtension")
36
37 class VcsGeneralSettingsConfigurable(val project: Project) : BoundCompositeConfigurable<UnnamedConfigurable>(
38   message("configurable.VcsGeneralConfigurationConfigurable.display.name"),
39   "project.propVCSSupport.Confirmation"
40 ), NoScroll, Configurable.WithEpDependencies {
41
42   override fun createConfigurables(): List<UnnamedConfigurable> =
43     VCS_SETTINGS_EP_NAME.getExtensions(project).mapNotNull { it.createConfigurable() }
44
45   override fun getDependencies() = listOf(VcsEP.EP_NAME, VCS_SETTINGS_EP_NAME)
46
47   override fun createPanel(): DialogPanel {
48     val vcsManager = ProjectLevelVcsManagerEx.getInstanceEx(project)
49     val vcsConfiguration = VcsConfiguration.getInstance(project)
50     val contentAnnotationSettings = VcsContentAnnotationSettings.getInstance(project)
51
52     val vcsListeners = mutableListOf<Runnable>()
53     fun updateActiveVcses() {
54       runInEdt {
55         vcsListeners.forEach { it.run() }
56       }
57     }
58     project.messageBus.connect(disposable!!).subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED,
59                                                        VcsListener { updateActiveVcses() })
60     VcsEP.EP_NAME.addChangeListener({ updateActiveVcses() }, disposable)
61
62     return panel {
63       titledRow(message("settings.general.confirmation.group.title")) {
64         fullRow {
65           val addConfirmation = vcsManager.getConfirmation(VcsConfiguration.StandardConfirmation.ADD)
66
67           label(message("settings.border.when.files.are.created"))
68             .withApplicableVcsesTooltip(addConfirmation, vcsListeners)
69           val addComboBox = comboBox(
70             EnumComboBoxModel(VcsShowConfirmationOption.Value::class.java),
71             { addConfirmation.value }, { addConfirmation.value = it },
72             renderer = listCellRenderer { value, _, _ ->
73               setText(when (value) {
74                         VcsShowConfirmationOption.Value.SHOW_CONFIRMATION -> message("radio.after.creation.show.options")
75                         VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY -> message("radio.after.creation.add.silently")
76                         VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY -> message("radio.after.creation.do.not.add")
77                         else -> ""
78                       })
79             })
80             .withApplicableVcsesTooltip(addConfirmation, vcsListeners)
81
82           checkBox(message("checkbox.including.files.created.outside.ide", ApplicationNamesInfo.getInstance().fullProductName),
83                    vcsConfiguration::ADD_EXTERNAL_FILES_SILENTLY)
84             .enableIf(OptionEnabledPredicate(addComboBox.component))
85             .withLargeLeftGap()
86         }
87         fullRow {
88           val removeConfirmation = vcsManager.getConfirmation(VcsConfiguration.StandardConfirmation.REMOVE)
89
90           label(message("settings.when.files.are.deleted"))
91             .withApplicableVcsesTooltip(removeConfirmation, vcsListeners)
92           comboBox(EnumComboBoxModel(VcsShowConfirmationOption.Value::class.java),
93                    { removeConfirmation.value }, { removeConfirmation.value = it },
94                    renderer = listCellRenderer { value, _, _ ->
95                      setText(when (value) {
96                                VcsShowConfirmationOption.Value.SHOW_CONFIRMATION -> message("radio.after.deletion.show.options")
97                                VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY -> message("radio.after.deletion.remove.silently")
98                                VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY -> message("radio.after.deletion.do.not.remove")
99                                else -> ""
100                              })
101                    })
102             .withApplicableVcsesTooltip(removeConfirmation, vcsListeners)
103         }
104         fullRow {
105           label(message("settings.general.show.options.before.command.label"))
106
107           for (setting in vcsManager.allOptions) {
108             checkBox(setting.displayName, { setting.value }, { setting.value = it })
109               .withLargeLeftGap()
110               .withApplicableVcsesTooltip(setting, vcsListeners)
111               .visibleIf(OptionVisibleForVcsesPredicate(project, setting, vcsListeners))
112           }
113         }
114         if (project.isDefault || ProjectLevelVcsManager.getInstance(project).allSupportedVcss.any { it.editFileProvider != null }) {
115           fullRow {
116             checkBox(cdShowReadOnlyStatusDialog(project))
117           }
118         }
119       }
120
121       titledRow(message("settings.general.changes.group.title")) {
122         fullRow {
123           val checkBox = checkBox(message("vcs.config.track.changed.on.server"),
124                                   vcsConfiguration::CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND)
125             .onApply {
126               if (!project.isDefault) {
127                 RemoteRevisionsCache.getInstance(project).updateAutomaticRefreshAlarmState(true)
128               }
129             }
130           spinner(vcsConfiguration::CHANGED_ON_SERVER_INTERVAL,
131                   5, 48 * 10 * 60, 5)
132             .enableIf(checkBox.selected)
133           @Suppress("DialogTitleCapitalization")
134           label(message("settings.check.every.minutes"))
135         }
136         fullRow {
137           val checkBox = checkBox(message("settings.checkbox.show.changed.in.last"),
138                                   { contentAnnotationSettings.isShow }, { contentAnnotationSettings.isShow = it })
139             .comment(message("settings.checkbox.show.changed.in.last.comment"))
140           spinner({ contentAnnotationSettings.limitDays }, { contentAnnotationSettings.limitDays = it },
141                   1, VcsContentAnnotationSettings.ourMaxDays, 1)
142             .enableIf(checkBox.selected)
143           @Suppress("DialogTitleCapitalization")
144           label(message("settings.checkbox.measure.days"))
145         }
146         fullRow {
147           checkBox(cdShowDirtyRecursively(project))
148         }
149       }
150
151       blockRow {
152         fullRow {
153           label(message("show.patch.in.explorer.after.creation.label"))
154           comboBox(EnumComboBoxModel(ShowPatchAfterCreationEnum::class.java),
155                    { ShowPatchAfterCreationEnum.getByState(vcsConfiguration.SHOW_PATCH_IN_EXPLORER) },
156                    { vcsConfiguration.SHOW_PATCH_IN_EXPLORER = it?.state })
157         }
158         fullRow {
159           checkBox(message("radio.restore.workspace.on.branch.switching"), vcsConfiguration::RELOAD_CONTEXT)
160             .comment(message("radio.restore.workspace.on.branch.switching.comment"))
161         }
162         fullRow {
163           val checkBox = checkBox(message("settings.checkbox.limit.history.to"), vcsConfiguration::LIMIT_HISTORY)
164           spinner(vcsConfiguration::MAXIMUM_HISTORY_ROWS, 10, 1000000, 10)
165             .enableIf(checkBox.selected)
166           @Suppress("DialogTitleCapitalization")
167           label(message("settings.checkbox.rows"))
168         }
169       }
170
171       for (configurable in configurables) {
172         appendDslConfigurableRow(configurable)
173       }
174     }
175   }
176
177   private fun <T : JComponent> CellBuilder<T>.withApplicableVcsesTooltip(setting: PersistentVcsSetting,
178                                                                          vcsListeners: MutableList<Runnable>): CellBuilder<T> {
179     vcsListeners.add(Runnable { updateApplicableVcsesTooltip(project, component, setting) })
180     updateApplicableVcsesTooltip(project, component, setting)
181     return this
182   }
183 }
184
185 private fun updateApplicableVcsesTooltip(project: Project, component: JComponent, setting: PersistentVcsSetting) {
186   val vcses = setting.getApplicableVcses(project)
187     .map { it.displayName }
188   component.toolTipText = when {
189     vcses.isNotEmpty() -> message("description.text.option.applicable.to.vcses", VcsUtil.joinWithAnd(vcses.toList(), 0))
190     else -> null
191   }
192 }
193
194 private enum class ShowPatchAfterCreationEnum(private val text: () -> @Nls String,
195                                               val state: Boolean?) {
196   ASK({ message("show.patch.in.explorer.after.creation.combobox.text.ask") }, null),
197   YES({ message("show.patch.in.explorer.after.creation.combobox.text.show.in.file.manager", RevealFileAction.getFileManagerName()) }, true),
198   NO({ message("show.patch.in.explorer.after.creation.combobox.text.no") }, false);
199
200   override fun toString(): String = text()
201
202   companion object {
203     fun getByState(state: Boolean?): ShowPatchAfterCreationEnum = when (state) {
204       null -> ASK
205       true -> YES
206       false -> NO
207     }
208   }
209 }
210
211 class OptionEnabledPredicate(private val comboBox: ComboBox<VcsShowConfirmationOption.Value>) : ComponentPredicate() {
212   override fun addListener(listener: (Boolean) -> Unit) {
213     comboBox.addItemListener { listener(invoke()) }
214   }
215
216   override fun invoke(): Boolean = comboBox.item == VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY
217 }
218
219 class OptionVisibleForVcsesPredicate(private val project: Project,
220                                      private val setting: PersistentVcsSetting,
221                                      private val vcsListeners: MutableList<Runnable>) : ComponentPredicate() {
222   override fun addListener(listener: (Boolean) -> Unit) {
223     vcsListeners.add(Runnable { listener(invoke()) })
224   }
225
226   override fun invoke(): Boolean {
227     return project.isDefault ||
228            setting.getApplicableVcses(project).isNotEmpty()
229   }
230 }