2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.jetbrains.settingsRepository
18 import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
19 import com.intellij.openapi.options.ConfigurableUi
20 import com.intellij.openapi.progress.ProgressIndicator
21 import com.intellij.openapi.progress.ProgressManager
22 import com.intellij.openapi.progress.Task
23 import com.intellij.openapi.ui.DialogBuilder
24 import com.intellij.openapi.ui.TextBrowseFolderListener
25 import com.intellij.openapi.ui.TextFieldWithBrowseButton
26 import com.intellij.openapi.util.text.StringUtil
27 import com.intellij.ui.DocumentAdapter
28 import com.intellij.util.Function
29 import com.intellij.util.containers.ContainerUtil
30 import com.intellij.util.deleteRecursively
31 import com.intellij.util.exists
32 import com.intellij.util.ui.FormBuilder
33 import com.intellij.util.ui.table.TableModelEditor
34 import gnu.trove.THashSet
35 import org.jetbrains.settingsRepository.git.asProgressMonitor
36 import org.jetbrains.settingsRepository.git.cloneBare
37 import javax.swing.JTextField
38 import javax.swing.event.DocumentEvent
40 private val COLUMNS = arrayOf(object : TableModelEditor.EditableColumnInfo<ReadonlySource, Boolean>() {
41 override fun getColumnClass() = Boolean::class.java
43 override fun valueOf(item: ReadonlySource) = item.active
45 override fun setValue(item: ReadonlySource, value: Boolean) {
49 object : TableModelEditor.EditableColumnInfo<ReadonlySource, String>() {
50 override fun valueOf(item: ReadonlySource) = item.url
52 override fun setValue(item: ReadonlySource, value: String) {
57 internal fun createReadOnlySourcesEditor(): ConfigurableUi<IcsSettings> {
58 val itemEditor = object : TableModelEditor.DialogItemEditor<ReadonlySource>() {
59 override fun clone(item: ReadonlySource, forInPlaceEditing: Boolean) = ReadonlySource(item.url, item.active)
61 override fun getItemClass() = ReadonlySource::class.java
63 override fun edit(item: ReadonlySource, mutator: Function<ReadonlySource, ReadonlySource>, isAdd: Boolean) {
64 val dialogBuilder = DialogBuilder()
65 val urlField = TextFieldWithBrowseButton(JTextField(20))
66 urlField.addBrowseFolderListener(TextBrowseFolderListener(FileChooserDescriptorFactory.createSingleFolderDescriptor()))
67 urlField.textField.document.addDocumentListener(object : DocumentAdapter() {
68 override fun textChanged(event: DocumentEvent) {
69 dialogBuilder.setOkActionEnabled(checkUrl(urlField.text.nullize()))
73 dialogBuilder.title("Add read-only source").resizable(false).centerPanel(FormBuilder.createFormBuilder().addLabeledComponent("URL:", urlField).panel).setPreferredFocusComponent(urlField)
74 if (dialogBuilder.showAndGet()) {
75 mutator.`fun`(item).url = urlField.text
79 override fun applyEdited(oldItem: ReadonlySource, newItem: ReadonlySource) {
80 newItem.url = oldItem.url
83 override fun isUseDialogToAdd() = true
86 val editor = TableModelEditor(COLUMNS, itemEditor, "No sources configured")
87 editor.reset(icsManager.settings.readOnlySources)
88 return object : ConfigurableUi<IcsSettings> {
89 override fun isModified(settings: IcsSettings) = editor.isModified
91 override fun apply(settings: IcsSettings) {
92 val oldList = settings.readOnlySources
93 val toDelete = THashSet<String>(oldList.size)
94 for (oldSource in oldList) {
95 ContainerUtil.addIfNotNull(toDelete, oldSource.path)
98 val toCheckout = THashSet<ReadonlySource>()
100 val newList = editor.apply()
101 for (newSource in newList) {
102 val path = newSource.path
103 if (path != null && !toDelete.remove(path)) {
104 toCheckout.add(newSource)
108 if (toDelete.isEmpty && toCheckout.isEmpty) {
112 ProgressManager.getInstance().run(object : Task.Modal(null, icsMessage("task.sync.title"), true) {
113 override fun run(indicator: ProgressIndicator) {
114 indicator.isIndeterminate = true
116 val root = icsManager.readOnlySourcesManager.rootDir
118 if (toDelete.isNotEmpty()) {
119 indicator.text = "Deleting old repositories"
120 for (path in toDelete) {
121 indicator.checkCanceled()
123 indicator.text2 = path
124 root.resolve(path).deleteRecursively()
126 catch (e: Exception) {
132 if (toCheckout.isNotEmpty()) {
133 for (source in toCheckout) {
134 indicator.checkCanceled()
136 indicator.text = "Cloning ${StringUtil.trimMiddle(source.url!!, 255)}"
137 val dir = root.resolve(source.path!!)
139 dir.deleteRecursively()
141 cloneBare(source.url!!, dir, icsManager.credentialsStore, indicator.asProgressMonitor()).close()
143 catch (e: Exception) {
149 icsManager.readOnlySourcesManager.setSources(newList)
154 override fun reset(settings: IcsSettings) {
155 editor.reset(settings.readOnlySources)
158 override fun getComponent() = editor.createComponent()