IDEA-155715 Settings Repository responds with confusing message
[idea/community.git] / plugins / settings-repository / src / settings / readOnlySourcesEditor.kt
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.jetbrains.settingsRepository
17
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
39
40 private val COLUMNS = arrayOf(object : TableModelEditor.EditableColumnInfo<ReadonlySource, Boolean>() {
41   override fun getColumnClass() = Boolean::class.java
42
43   override fun valueOf(item: ReadonlySource) = item.active
44
45   override fun setValue(item: ReadonlySource, value: Boolean) {
46     item.active = value
47   }
48 },
49     object : TableModelEditor.EditableColumnInfo<ReadonlySource, String>() {
50       override fun valueOf(item: ReadonlySource) = item.url
51
52       override fun setValue(item: ReadonlySource, value: String) {
53         item.url = value
54       }
55     })
56
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)
60
61     override fun getItemClass() = ReadonlySource::class.java
62
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()))
70         }
71       })
72
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
76       }
77     }
78
79     override fun applyEdited(oldItem: ReadonlySource, newItem: ReadonlySource) {
80       newItem.url = oldItem.url
81     }
82
83     override fun isUseDialogToAdd() = true
84   }
85
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
90
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)
96       }
97
98       val toCheckout = THashSet<ReadonlySource>()
99
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)
105         }
106       }
107
108       if (toDelete.isEmpty && toCheckout.isEmpty) {
109         return
110       }
111
112       ProgressManager.getInstance().run(object : Task.Modal(null, icsMessage("task.sync.title"), true) {
113         override fun run(indicator: ProgressIndicator) {
114           indicator.isIndeterminate = true
115
116           val root = icsManager.readOnlySourcesManager.rootDir
117
118           if (toDelete.isNotEmpty()) {
119             indicator.text = "Deleting old repositories"
120             for (path in toDelete) {
121               indicator.checkCanceled()
122               try {
123                 indicator.text2 = path
124                 root.resolve(path).deleteRecursively()
125               }
126               catch (e: Exception) {
127                 LOG.error(e)
128               }
129             }
130           }
131
132           if (toCheckout.isNotEmpty()) {
133             for (source in toCheckout) {
134               indicator.checkCanceled()
135               try {
136                 indicator.text = "Cloning ${StringUtil.trimMiddle(source.url!!, 255)}"
137                 val dir = root.resolve(source.path!!)
138                 if (dir.exists()) {
139                   dir.deleteRecursively()
140                 }
141                 cloneBare(source.url!!, dir, icsManager.credentialsStore, indicator.asProgressMonitor()).close()
142               }
143               catch (e: Exception) {
144                 LOG.error(e)
145               }
146             }
147           }
148
149           icsManager.readOnlySourcesManager.setSources(newList)
150         }
151       })
152     }
153
154     override fun reset(settings: IcsSettings) {
155       editor.reset(settings.readOnlySources)
156     }
157
158     override fun getComponent() = editor.createComponent()
159   }
160 }